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,
11945 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11946 * field's data value (defaults to the underlying DOM element's name)
11948 hiddenName: undefined,
11950 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11954 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11956 selectedClass: 'active',
11959 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11963 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11964 * anchor positions (defaults to 'tl-bl')
11966 listAlign: 'tl-bl?',
11968 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11972 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11973 * query specified by the allQuery config option (defaults to 'query')
11975 triggerAction: 'query',
11977 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11978 * (defaults to 4, does not apply if editable = false)
11982 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11983 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11987 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11988 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11992 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11993 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11997 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11998 * when editable = true (defaults to false)
12000 selectOnFocus:false,
12002 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12004 queryParam: 'query',
12006 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12007 * when mode = 'remote' (defaults to 'Loading...')
12009 loadingText: 'Loading...',
12011 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12015 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12019 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12020 * traditional select (defaults to true)
12024 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12028 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12032 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12033 * listWidth has a higher value)
12037 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12038 * allow the user to set arbitrary text into the field (defaults to false)
12040 forceSelection:false,
12042 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12043 * if typeAhead = true (defaults to 250)
12045 typeAheadDelay : 250,
12047 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12048 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12050 valueNotFoundText : undefined,
12052 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12054 blockFocus : false,
12057 * @cfg {Boolean} disableClear Disable showing of clear button.
12059 disableClear : false,
12061 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12063 alwaysQuery : false,
12066 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12071 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12073 invalidClass : "has-warning",
12076 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12078 validClass : "has-success",
12081 * @cfg {Boolean} specialFilter (true|false) special filter default false
12083 specialFilter : false,
12086 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12088 mobileTouchView : true,
12100 btnPosition : 'right',
12101 triggerList : true,
12102 showToggleBtn : true,
12104 emptyResultText: 'Empty',
12105 triggerText : 'Select',
12107 // element that contains real text value.. (when hidden is used..)
12109 getAutoCreate : function()
12117 if(Roo.isTouch && this.mobileTouchView){
12118 cfg = this.getAutoCreateTouchView();
12125 if(!this.tickable){
12126 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12131 * ComboBox with tickable selections
12134 var align = this.labelAlign || this.parentLabelAlign();
12137 cls : 'form-group roo-combobox-tickable' //input-group
12142 cls : 'tickable-buttons',
12147 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12148 html : this.triggerText
12154 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12161 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12168 buttons.cn.unshift({
12170 cls: 'roo-select2-search-field-input'
12176 Roo.each(buttons.cn, function(c){
12178 c.cls += ' btn-' + _this.size;
12181 if (_this.disabled) {
12192 cls: 'form-hidden-field'
12196 cls: 'roo-select2-choices',
12200 cls: 'roo-select2-search-field',
12212 cls: 'roo-select2-container input-group roo-select2-container-multi',
12217 // cls: 'typeahead typeahead-long dropdown-menu',
12218 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12223 if(this.hasFeedback && !this.allowBlank){
12227 cls: 'glyphicon form-control-feedback'
12230 combobox.cn.push(feedback);
12233 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12235 // Roo.log("left and has label");
12241 cls : 'control-label col-sm-' + this.labelWidth,
12242 html : this.fieldLabel
12246 cls : "col-sm-" + (12 - this.labelWidth),
12253 } else if ( this.fieldLabel.length) {
12254 // Roo.log(" label");
12259 //cls : 'input-group-addon',
12260 html : this.fieldLabel
12270 // Roo.log(" no label && no align");
12277 ['xs','sm','md','lg'].map(function(size){
12278 if (settings[size]) {
12279 cfg.cls += ' col-' + size + '-' + settings[size];
12287 _initEventsCalled : false,
12290 initEvents: function()
12293 if (this._initEventsCalled) { // as we call render... prevent looping...
12296 this._initEventsCalled = true;
12299 throw "can not find store for combo";
12302 this.store = Roo.factory(this.store, Roo.data);
12304 // if we are building from html. then this element is so complex, that we can not really
12305 // use the rendered HTML.
12306 // so we have to trash and replace the previous code.
12307 if (Roo.XComponent.build_from_html) {
12309 // remove this element....
12310 var e = this.el.dom, k=0;
12311 while (e ) { e = e.previousSibling; ++k;}
12316 this.rendered = false;
12318 this.render(this.parent().getChildContainer(true), k);
12329 if(Roo.isTouch && this.mobileTouchView){
12330 this.initTouchView();
12335 this.initTickableEvents();
12339 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12341 if(this.hiddenName){
12343 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12345 this.hiddenField.dom.value =
12346 this.hiddenValue !== undefined ? this.hiddenValue :
12347 this.value !== undefined ? this.value : '';
12349 // prevent input submission
12350 this.el.dom.removeAttribute('name');
12351 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12356 // this.el.dom.setAttribute('autocomplete', 'off');
12359 var cls = 'x-combo-list';
12361 //this.list = new Roo.Layer({
12362 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12368 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12369 _this.list.setWidth(lw);
12372 this.list.on('mouseover', this.onViewOver, this);
12373 this.list.on('mousemove', this.onViewMove, this);
12375 this.list.on('scroll', this.onViewScroll, this);
12378 this.list.swallowEvent('mousewheel');
12379 this.assetHeight = 0;
12382 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12383 this.assetHeight += this.header.getHeight();
12386 this.innerList = this.list.createChild({cls:cls+'-inner'});
12387 this.innerList.on('mouseover', this.onViewOver, this);
12388 this.innerList.on('mousemove', this.onViewMove, this);
12389 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12391 if(this.allowBlank && !this.pageSize && !this.disableClear){
12392 this.footer = this.list.createChild({cls:cls+'-ft'});
12393 this.pageTb = new Roo.Toolbar(this.footer);
12397 this.footer = this.list.createChild({cls:cls+'-ft'});
12398 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12399 {pageSize: this.pageSize});
12403 if (this.pageTb && this.allowBlank && !this.disableClear) {
12405 this.pageTb.add(new Roo.Toolbar.Fill(), {
12406 cls: 'x-btn-icon x-btn-clear',
12408 handler: function()
12411 _this.clearValue();
12412 _this.onSelect(false, -1);
12417 this.assetHeight += this.footer.getHeight();
12422 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12425 this.view = new Roo.View(this.list, this.tpl, {
12426 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12428 //this.view.wrapEl.setDisplayed(false);
12429 this.view.on('click', this.onViewClick, this);
12433 this.store.on('beforeload', this.onBeforeLoad, this);
12434 this.store.on('load', this.onLoad, this);
12435 this.store.on('loadexception', this.onLoadException, this);
12437 if(this.resizable){
12438 this.resizer = new Roo.Resizable(this.list, {
12439 pinned:true, handles:'se'
12441 this.resizer.on('resize', function(r, w, h){
12442 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12443 this.listWidth = w;
12444 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12445 this.restrictHeight();
12447 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12450 if(!this.editable){
12451 this.editable = true;
12452 this.setEditable(false);
12457 if (typeof(this.events.add.listeners) != 'undefined') {
12459 this.addicon = this.wrap.createChild(
12460 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12462 this.addicon.on('click', function(e) {
12463 this.fireEvent('add', this);
12466 if (typeof(this.events.edit.listeners) != 'undefined') {
12468 this.editicon = this.wrap.createChild(
12469 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12470 if (this.addicon) {
12471 this.editicon.setStyle('margin-left', '40px');
12473 this.editicon.on('click', function(e) {
12475 // we fire even if inothing is selected..
12476 this.fireEvent('edit', this, this.lastData );
12482 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12483 "up" : function(e){
12484 this.inKeyMode = true;
12488 "down" : function(e){
12489 if(!this.isExpanded()){
12490 this.onTriggerClick();
12492 this.inKeyMode = true;
12497 "enter" : function(e){
12498 // this.onViewClick();
12502 if(this.fireEvent("specialkey", this, e)){
12503 this.onViewClick(false);
12509 "esc" : function(e){
12513 "tab" : function(e){
12516 if(this.fireEvent("specialkey", this, e)){
12517 this.onViewClick(false);
12525 doRelay : function(foo, bar, hname){
12526 if(hname == 'down' || this.scope.isExpanded()){
12527 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12536 this.queryDelay = Math.max(this.queryDelay || 10,
12537 this.mode == 'local' ? 10 : 250);
12540 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12542 if(this.typeAhead){
12543 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12545 if(this.editable !== false){
12546 this.inputEl().on("keyup", this.onKeyUp, this);
12548 if(this.forceSelection){
12549 this.inputEl().on('blur', this.doForce, this);
12553 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12554 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12558 initTickableEvents: function()
12562 if(this.hiddenName){
12564 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12566 this.hiddenField.dom.value =
12567 this.hiddenValue !== undefined ? this.hiddenValue :
12568 this.value !== undefined ? this.value : '';
12570 // prevent input submission
12571 this.el.dom.removeAttribute('name');
12572 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12577 // this.list = this.el.select('ul.dropdown-menu',true).first();
12579 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12580 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12581 if(this.triggerList){
12582 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12585 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12586 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12588 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12589 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12591 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12592 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12594 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12595 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12596 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12599 this.cancelBtn.hide();
12604 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12605 _this.list.setWidth(lw);
12608 this.list.on('mouseover', this.onViewOver, this);
12609 this.list.on('mousemove', this.onViewMove, this);
12611 this.list.on('scroll', this.onViewScroll, this);
12614 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>';
12617 this.view = new Roo.View(this.list, this.tpl, {
12618 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12621 //this.view.wrapEl.setDisplayed(false);
12622 this.view.on('click', this.onViewClick, this);
12626 this.store.on('beforeload', this.onBeforeLoad, this);
12627 this.store.on('load', this.onLoad, this);
12628 this.store.on('loadexception', this.onLoadException, this);
12631 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12632 "up" : function(e){
12633 this.inKeyMode = true;
12637 "down" : function(e){
12638 this.inKeyMode = true;
12642 "enter" : function(e){
12643 if(this.fireEvent("specialkey", this, e)){
12644 this.onViewClick(false);
12650 "esc" : function(e){
12651 this.onTickableFooterButtonClick(e, false, false);
12654 "tab" : function(e){
12655 this.fireEvent("specialkey", this, e);
12657 this.onTickableFooterButtonClick(e, false, false);
12664 doRelay : function(e, fn, key){
12665 if(this.scope.isExpanded()){
12666 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12675 this.queryDelay = Math.max(this.queryDelay || 10,
12676 this.mode == 'local' ? 10 : 250);
12679 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12681 if(this.typeAhead){
12682 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12685 if(this.editable !== false){
12686 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12691 onDestroy : function(){
12693 this.view.setStore(null);
12694 this.view.el.removeAllListeners();
12695 this.view.el.remove();
12696 this.view.purgeListeners();
12699 this.list.dom.innerHTML = '';
12703 this.store.un('beforeload', this.onBeforeLoad, this);
12704 this.store.un('load', this.onLoad, this);
12705 this.store.un('loadexception', this.onLoadException, this);
12707 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12711 fireKey : function(e){
12712 if(e.isNavKeyPress() && !this.list.isVisible()){
12713 this.fireEvent("specialkey", this, e);
12718 onResize: function(w, h){
12719 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12721 // if(typeof w != 'number'){
12722 // // we do not handle it!?!?
12725 // var tw = this.trigger.getWidth();
12726 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12727 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12729 // this.inputEl().setWidth( this.adjustWidth('input', x));
12731 // //this.trigger.setStyle('left', x+'px');
12733 // if(this.list && this.listWidth === undefined){
12734 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12735 // this.list.setWidth(lw);
12736 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12744 * Allow or prevent the user from directly editing the field text. If false is passed,
12745 * the user will only be able to select from the items defined in the dropdown list. This method
12746 * is the runtime equivalent of setting the 'editable' config option at config time.
12747 * @param {Boolean} value True to allow the user to directly edit the field text
12749 setEditable : function(value){
12750 if(value == this.editable){
12753 this.editable = value;
12755 this.inputEl().dom.setAttribute('readOnly', true);
12756 this.inputEl().on('mousedown', this.onTriggerClick, this);
12757 this.inputEl().addClass('x-combo-noedit');
12759 this.inputEl().dom.setAttribute('readOnly', false);
12760 this.inputEl().un('mousedown', this.onTriggerClick, this);
12761 this.inputEl().removeClass('x-combo-noedit');
12767 onBeforeLoad : function(combo,opts){
12768 if(!this.hasFocus){
12772 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12774 this.restrictHeight();
12775 this.selectedIndex = -1;
12779 onLoad : function(){
12781 this.hasQuery = false;
12783 if(!this.hasFocus){
12787 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12788 this.loading.hide();
12791 if(this.store.getCount() > 0){
12793 this.restrictHeight();
12794 if(this.lastQuery == this.allQuery){
12795 if(this.editable && !this.tickable){
12796 this.inputEl().dom.select();
12800 !this.selectByValue(this.value, true) &&
12803 !this.store.lastOptions ||
12804 typeof(this.store.lastOptions.add) == 'undefined' ||
12805 this.store.lastOptions.add != true
12808 this.select(0, true);
12811 if(this.autoFocus){
12814 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12815 this.taTask.delay(this.typeAheadDelay);
12819 this.onEmptyResults();
12825 onLoadException : function()
12827 this.hasQuery = false;
12829 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12830 this.loading.hide();
12833 if(this.tickable && this.editable){
12838 // only causes errors at present
12839 //Roo.log(this.store.reader.jsonData);
12840 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12842 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12848 onTypeAhead : function(){
12849 if(this.store.getCount() > 0){
12850 var r = this.store.getAt(0);
12851 var newValue = r.data[this.displayField];
12852 var len = newValue.length;
12853 var selStart = this.getRawValue().length;
12855 if(selStart != len){
12856 this.setRawValue(newValue);
12857 this.selectText(selStart, newValue.length);
12863 onSelect : function(record, index){
12865 if(this.fireEvent('beforeselect', this, record, index) !== false){
12867 this.setFromData(index > -1 ? record.data : false);
12870 this.fireEvent('select', this, record, index);
12875 * Returns the currently selected field value or empty string if no value is set.
12876 * @return {String} value The selected value
12878 getValue : function(){
12881 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12884 if(this.valueField){
12885 return typeof this.value != 'undefined' ? this.value : '';
12887 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12892 * Clears any text/value currently set in the field
12894 clearValue : function(){
12895 if(this.hiddenField){
12896 this.hiddenField.dom.value = '';
12899 this.setRawValue('');
12900 this.lastSelectionText = '';
12901 this.lastData = false;
12903 var close = this.closeTriggerEl();
12912 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12913 * will be displayed in the field. If the value does not match the data value of an existing item,
12914 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12915 * Otherwise the field will be blank (although the value will still be set).
12916 * @param {String} value The value to match
12918 setValue : function(v){
12925 if(this.valueField){
12926 var r = this.findRecord(this.valueField, v);
12928 text = r.data[this.displayField];
12929 }else if(this.valueNotFoundText !== undefined){
12930 text = this.valueNotFoundText;
12933 this.lastSelectionText = text;
12934 if(this.hiddenField){
12935 this.hiddenField.dom.value = v;
12937 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12940 var close = this.closeTriggerEl();
12943 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12947 * @property {Object} the last set data for the element
12952 * Sets the value of the field based on a object which is related to the record format for the store.
12953 * @param {Object} value the value to set as. or false on reset?
12955 setFromData : function(o){
12962 var dv = ''; // display value
12963 var vv = ''; // value value..
12965 if (this.displayField) {
12966 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12968 // this is an error condition!!!
12969 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12972 if(this.valueField){
12973 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12976 var close = this.closeTriggerEl();
12979 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12982 if(this.hiddenField){
12983 this.hiddenField.dom.value = vv;
12985 this.lastSelectionText = dv;
12986 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12990 // no hidden field.. - we store the value in 'value', but still display
12991 // display field!!!!
12992 this.lastSelectionText = dv;
12993 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13000 reset : function(){
13001 // overridden so that last data is reset..
13008 this.setValue(this.originalValue);
13009 this.clearInvalid();
13010 this.lastData = false;
13012 this.view.clearSelections();
13016 findRecord : function(prop, value){
13018 if(this.store.getCount() > 0){
13019 this.store.each(function(r){
13020 if(r.data[prop] == value){
13030 getName: function()
13032 // returns hidden if it's set..
13033 if (!this.rendered) {return ''};
13034 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13038 onViewMove : function(e, t){
13039 this.inKeyMode = false;
13043 onViewOver : function(e, t){
13044 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13047 var item = this.view.findItemFromChild(t);
13050 var index = this.view.indexOf(item);
13051 this.select(index, false);
13056 onViewClick : function(view, doFocus, el, e)
13058 var index = this.view.getSelectedIndexes()[0];
13060 var r = this.store.getAt(index);
13064 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13071 Roo.each(this.tickItems, function(v,k){
13073 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13075 _this.tickItems.splice(k, 1);
13077 if(typeof(e) == 'undefined' && view == false){
13078 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13090 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13091 this.tickItems.push(r.data);
13094 if(typeof(e) == 'undefined' && view == false){
13095 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13102 this.onSelect(r, index);
13104 if(doFocus !== false && !this.blockFocus){
13105 this.inputEl().focus();
13110 restrictHeight : function(){
13111 //this.innerList.dom.style.height = '';
13112 //var inner = this.innerList.dom;
13113 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13114 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13115 //this.list.beginUpdate();
13116 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13117 this.list.alignTo(this.inputEl(), this.listAlign);
13118 this.list.alignTo(this.inputEl(), this.listAlign);
13119 //this.list.endUpdate();
13123 onEmptyResults : function(){
13125 if(this.tickable && this.editable){
13126 this.restrictHeight();
13134 * Returns true if the dropdown list is expanded, else false.
13136 isExpanded : function(){
13137 return this.list.isVisible();
13141 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13142 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13143 * @param {String} value The data value of the item to select
13144 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13145 * selected item if it is not currently in view (defaults to true)
13146 * @return {Boolean} True if the value matched an item in the list, else false
13148 selectByValue : function(v, scrollIntoView){
13149 if(v !== undefined && v !== null){
13150 var r = this.findRecord(this.valueField || this.displayField, v);
13152 this.select(this.store.indexOf(r), scrollIntoView);
13160 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13161 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13162 * @param {Number} index The zero-based index of the list item to select
13163 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13164 * selected item if it is not currently in view (defaults to true)
13166 select : function(index, scrollIntoView){
13167 this.selectedIndex = index;
13168 this.view.select(index);
13169 if(scrollIntoView !== false){
13170 var el = this.view.getNode(index);
13172 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13175 this.list.scrollChildIntoView(el, false);
13181 selectNext : function(){
13182 var ct = this.store.getCount();
13184 if(this.selectedIndex == -1){
13186 }else if(this.selectedIndex < ct-1){
13187 this.select(this.selectedIndex+1);
13193 selectPrev : function(){
13194 var ct = this.store.getCount();
13196 if(this.selectedIndex == -1){
13198 }else if(this.selectedIndex != 0){
13199 this.select(this.selectedIndex-1);
13205 onKeyUp : function(e){
13206 if(this.editable !== false && !e.isSpecialKey()){
13207 this.lastKey = e.getKey();
13208 this.dqTask.delay(this.queryDelay);
13213 validateBlur : function(){
13214 return !this.list || !this.list.isVisible();
13218 initQuery : function(){
13220 var v = this.getRawValue();
13222 if(this.tickable && this.editable){
13223 v = this.tickableInputEl().getValue();
13230 doForce : function(){
13231 if(this.inputEl().dom.value.length > 0){
13232 this.inputEl().dom.value =
13233 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13239 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13240 * query allowing the query action to be canceled if needed.
13241 * @param {String} query The SQL query to execute
13242 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13243 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13244 * saved in the current store (defaults to false)
13246 doQuery : function(q, forceAll){
13248 if(q === undefined || q === null){
13253 forceAll: forceAll,
13257 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13262 forceAll = qe.forceAll;
13263 if(forceAll === true || (q.length >= this.minChars)){
13265 this.hasQuery = true;
13267 if(this.lastQuery != q || this.alwaysQuery){
13268 this.lastQuery = q;
13269 if(this.mode == 'local'){
13270 this.selectedIndex = -1;
13272 this.store.clearFilter();
13275 if(this.specialFilter){
13276 this.fireEvent('specialfilter', this);
13281 this.store.filter(this.displayField, q);
13284 this.store.fireEvent("datachanged", this.store);
13291 this.store.baseParams[this.queryParam] = q;
13293 var options = {params : this.getParams(q)};
13296 options.add = true;
13297 options.params.start = this.page * this.pageSize;
13300 this.store.load(options);
13303 * this code will make the page width larger, at the beginning, the list not align correctly,
13304 * we should expand the list on onLoad
13305 * so command out it
13310 this.selectedIndex = -1;
13315 this.loadNext = false;
13319 getParams : function(q){
13321 //p[this.queryParam] = q;
13325 p.limit = this.pageSize;
13331 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13333 collapse : function(){
13334 if(!this.isExpanded()){
13341 this.hasFocus = false;
13343 this.cancelBtn.hide();
13344 this.trigger.show();
13347 this.tickableInputEl().dom.value = '';
13348 this.tickableInputEl().blur();
13353 Roo.get(document).un('mousedown', this.collapseIf, this);
13354 Roo.get(document).un('mousewheel', this.collapseIf, this);
13355 if (!this.editable) {
13356 Roo.get(document).un('keydown', this.listKeyPress, this);
13358 this.fireEvent('collapse', this);
13362 collapseIf : function(e){
13363 var in_combo = e.within(this.el);
13364 var in_list = e.within(this.list);
13365 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13367 if (in_combo || in_list || is_list) {
13368 //e.stopPropagation();
13373 this.onTickableFooterButtonClick(e, false, false);
13381 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13383 expand : function(){
13385 if(this.isExpanded() || !this.hasFocus){
13389 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13390 this.list.setWidth(lw);
13397 this.restrictHeight();
13401 this.tickItems = Roo.apply([], this.item);
13404 this.cancelBtn.show();
13405 this.trigger.hide();
13408 this.tickableInputEl().focus();
13413 Roo.get(document).on('mousedown', this.collapseIf, this);
13414 Roo.get(document).on('mousewheel', this.collapseIf, this);
13415 if (!this.editable) {
13416 Roo.get(document).on('keydown', this.listKeyPress, this);
13419 this.fireEvent('expand', this);
13423 // Implements the default empty TriggerField.onTriggerClick function
13424 onTriggerClick : function(e)
13426 Roo.log('trigger click');
13428 if(this.disabled || !this.triggerList){
13433 this.loadNext = false;
13435 if(this.isExpanded()){
13437 if (!this.blockFocus) {
13438 this.inputEl().focus();
13442 this.hasFocus = true;
13443 if(this.triggerAction == 'all') {
13444 this.doQuery(this.allQuery, true);
13446 this.doQuery(this.getRawValue());
13448 if (!this.blockFocus) {
13449 this.inputEl().focus();
13454 onTickableTriggerClick : function(e)
13461 this.loadNext = false;
13462 this.hasFocus = true;
13464 if(this.triggerAction == 'all') {
13465 this.doQuery(this.allQuery, true);
13467 this.doQuery(this.getRawValue());
13471 onSearchFieldClick : function(e)
13473 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13474 this.onTickableFooterButtonClick(e, false, false);
13478 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13483 this.loadNext = false;
13484 this.hasFocus = true;
13486 if(this.triggerAction == 'all') {
13487 this.doQuery(this.allQuery, true);
13489 this.doQuery(this.getRawValue());
13493 listKeyPress : function(e)
13495 //Roo.log('listkeypress');
13496 // scroll to first matching element based on key pres..
13497 if (e.isSpecialKey()) {
13500 var k = String.fromCharCode(e.getKey()).toUpperCase();
13503 var csel = this.view.getSelectedNodes();
13504 var cselitem = false;
13506 var ix = this.view.indexOf(csel[0]);
13507 cselitem = this.store.getAt(ix);
13508 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13514 this.store.each(function(v) {
13516 // start at existing selection.
13517 if (cselitem.id == v.id) {
13523 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13524 match = this.store.indexOf(v);
13530 if (match === false) {
13531 return true; // no more action?
13534 this.view.select(match);
13535 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13536 sn.scrollIntoView(sn.dom.parentNode, false);
13539 onViewScroll : function(e, t){
13541 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){
13545 this.hasQuery = true;
13547 this.loading = this.list.select('.loading', true).first();
13549 if(this.loading === null){
13550 this.list.createChild({
13552 cls: 'loading roo-select2-more-results roo-select2-active',
13553 html: 'Loading more results...'
13556 this.loading = this.list.select('.loading', true).first();
13558 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13560 this.loading.hide();
13563 this.loading.show();
13568 this.loadNext = true;
13570 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13575 addItem : function(o)
13577 var dv = ''; // display value
13579 if (this.displayField) {
13580 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13582 // this is an error condition!!!
13583 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13590 var choice = this.choices.createChild({
13592 cls: 'roo-select2-search-choice',
13601 cls: 'roo-select2-search-choice-close',
13606 }, this.searchField);
13608 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13610 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13618 this.inputEl().dom.value = '';
13623 onRemoveItem : function(e, _self, o)
13625 e.preventDefault();
13627 this.lastItem = Roo.apply([], this.item);
13629 var index = this.item.indexOf(o.data) * 1;
13632 Roo.log('not this item?!');
13636 this.item.splice(index, 1);
13641 this.fireEvent('remove', this, e);
13647 syncValue : function()
13649 if(!this.item.length){
13656 Roo.each(this.item, function(i){
13657 if(_this.valueField){
13658 value.push(i[_this.valueField]);
13665 this.value = value.join(',');
13667 if(this.hiddenField){
13668 this.hiddenField.dom.value = this.value;
13671 this.store.fireEvent("datachanged", this.store);
13674 clearItem : function()
13676 if(!this.multiple){
13682 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13690 if(this.tickable && !Roo.isTouch){
13691 this.view.refresh();
13695 inputEl: function ()
13697 if(Roo.isTouch && this.mobileTouchView){
13698 return this.el.select('input.form-control',true).first();
13702 return this.searchField;
13705 return this.el.select('input.form-control',true).first();
13709 onTickableFooterButtonClick : function(e, btn, el)
13711 e.preventDefault();
13713 this.lastItem = Roo.apply([], this.item);
13715 if(btn && btn.name == 'cancel'){
13716 this.tickItems = Roo.apply([], this.item);
13725 Roo.each(this.tickItems, function(o){
13733 validate : function()
13735 var v = this.getRawValue();
13738 v = this.getValue();
13741 if(this.disabled || this.allowBlank || v.length){
13746 this.markInvalid();
13750 tickableInputEl : function()
13752 if(!this.tickable || !this.editable){
13753 return this.inputEl();
13756 return this.inputEl().select('.roo-select2-search-field-input', true).first();
13760 getAutoCreateTouchView : function()
13765 cls: 'form-group' //input-group
13771 type : this.inputType,
13772 cls : 'form-control x-combo-noedit',
13773 autocomplete: 'new-password',
13774 placeholder : this.placeholder || '',
13779 input.name = this.name;
13783 input.cls += ' input-' + this.size;
13786 if (this.disabled) {
13787 input.disabled = true;
13798 inputblock.cls += ' input-group';
13800 inputblock.cn.unshift({
13802 cls : 'input-group-addon',
13807 if(this.removable && !this.multiple){
13808 inputblock.cls += ' roo-removable';
13810 inputblock.cn.push({
13813 cls : 'roo-combo-removable-btn close'
13817 if(this.hasFeedback && !this.allowBlank){
13819 inputblock.cls += ' has-feedback';
13821 inputblock.cn.push({
13823 cls: 'glyphicon form-control-feedback'
13830 inputblock.cls += (this.before) ? '' : ' input-group';
13832 inputblock.cn.push({
13834 cls : 'input-group-addon',
13845 cls: 'form-hidden-field'
13859 cls: 'form-hidden-field'
13863 cls: 'roo-select2-choices',
13867 cls: 'roo-select2-search-field',
13880 cls: 'roo-select2-container input-group roo-touchview-combobox ',
13886 if(!this.multiple && this.showToggleBtn){
13893 if (this.caret != false) {
13896 cls: 'fa fa-' + this.caret
13903 cls : 'input-group-addon btn dropdown-toggle',
13908 cls: 'combobox-clear',
13922 combobox.cls += ' roo-select2-container-multi';
13925 var align = this.labelAlign || this.parentLabelAlign();
13929 if(this.fieldLabel.length && this.labelWidth){
13931 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13932 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13937 cls : 'control-label ' + lw,
13938 html : this.fieldLabel
13950 var settings = this;
13952 ['xs','sm','md','lg'].map(function(size){
13953 if (settings[size]) {
13954 cfg.cls += ' col-' + size + '-' + settings[size];
13961 initTouchView : function()
13963 this.renderTouchView();
13965 this.touchViewEl.on('scroll', function(){
13966 this.el.dom.scrollTop = 0;
13969 this.originalValue = this.getValue();
13971 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
13973 this.inputEl().on("click", this.showTouchView, this);
13974 this.triggerEl.on("click", this.showTouchView, this);
13976 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13977 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13979 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13981 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13982 this.store.on('load', this.onTouchViewLoad, this);
13983 this.store.on('loadexception', this.onTouchViewLoadException, this);
13985 if(this.hiddenName){
13987 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13989 this.hiddenField.dom.value =
13990 this.hiddenValue !== undefined ? this.hiddenValue :
13991 this.value !== undefined ? this.value : '';
13993 this.el.dom.removeAttribute('name');
13994 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13998 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13999 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14002 if(this.removable && !this.multiple){
14003 var close = this.closeTriggerEl();
14005 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14006 close.on('click', this.removeBtnClick, this, close);
14010 * fix the bug in Safari iOS8
14012 this.inputEl().on("focus", function(e){
14013 document.activeElement.blur();
14021 renderTouchView : function()
14023 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14024 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14026 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14027 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14029 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14030 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14031 this.touchViewBodyEl.setStyle('overflow', 'auto');
14033 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14034 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14036 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14037 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14041 showTouchView : function()
14047 this.touchViewHeaderEl.hide();
14049 if(this.fieldLabel.length){
14050 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
14051 this.touchViewHeaderEl.show();
14054 this.touchViewEl.show();
14056 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14057 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14058 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14060 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14062 if(this.fieldLabel.length){
14063 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14066 this.touchViewBodyEl.setHeight(bodyHeight);
14070 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14072 this.touchViewEl.addClass('in');
14075 this.doTouchViewQuery();
14079 hideTouchView : function()
14081 this.touchViewEl.removeClass('in');
14085 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14087 this.touchViewEl.setStyle('display', 'none');
14092 setTouchViewValue : function()
14099 Roo.each(this.tickItems, function(o){
14104 this.hideTouchView();
14107 doTouchViewQuery : function()
14116 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14120 if(!this.alwaysQuery || this.mode == 'local'){
14121 this.onTouchViewLoad();
14128 onTouchViewBeforeLoad : function(combo,opts)
14134 onTouchViewLoad : function()
14136 if(this.store.getCount() < 1){
14137 this.onTouchViewEmptyResults();
14141 this.clearTouchView();
14143 var rawValue = this.getRawValue();
14145 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14147 this.tickItems = [];
14149 this.store.data.each(function(d, rowIndex){
14150 var row = this.touchViewListGroup.createChild(template);
14152 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14153 row.addClass(d.data.cls);
14156 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14159 html : d.data[this.displayField]
14162 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14163 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14166 row.removeClass('selected');
14167 if(!this.multiple && this.valueField &&
14168 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14171 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14172 row.addClass('selected');
14175 if(this.multiple && this.valueField &&
14176 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14180 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14181 this.tickItems.push(d.data);
14184 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14188 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14190 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14192 if(this.fieldLabel.length){
14193 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14196 var listHeight = this.touchViewListGroup.getHeight();
14200 if(firstChecked && listHeight > bodyHeight){
14201 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14206 onTouchViewLoadException : function()
14208 this.hideTouchView();
14211 onTouchViewEmptyResults : function()
14213 this.clearTouchView();
14215 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14217 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14221 clearTouchView : function()
14223 this.touchViewListGroup.dom.innerHTML = '';
14226 onTouchViewClick : function(e, el, o)
14228 e.preventDefault();
14231 var rowIndex = o.rowIndex;
14233 var r = this.store.getAt(rowIndex);
14235 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14237 if(!this.multiple){
14238 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14239 c.dom.removeAttribute('checked');
14242 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14244 this.setFromData(r.data);
14246 var close = this.closeTriggerEl();
14252 this.hideTouchView();
14254 this.fireEvent('select', this, r, rowIndex);
14259 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14260 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14261 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14265 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14266 this.addItem(r.data);
14267 this.tickItems.push(r.data);
14273 * @cfg {Boolean} grow
14277 * @cfg {Number} growMin
14281 * @cfg {Number} growMax
14290 Roo.apply(Roo.bootstrap.ComboBox, {
14294 cls: 'modal-header',
14316 cls: 'list-group-item',
14320 cls: 'roo-combobox-list-group-item-value'
14324 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14338 listItemCheckbox : {
14340 cls: 'list-group-item',
14344 cls: 'roo-combobox-list-group-item-value'
14348 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14364 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14369 cls: 'modal-footer',
14377 cls: 'col-xs-6 text-left',
14380 cls: 'btn btn-danger roo-touch-view-cancel',
14386 cls: 'col-xs-6 text-right',
14389 cls: 'btn btn-success roo-touch-view-ok',
14400 Roo.apply(Roo.bootstrap.ComboBox, {
14402 touchViewTemplate : {
14404 cls: 'modal fade roo-combobox-touch-view',
14408 cls: 'modal-dialog',
14409 style : 'position:fixed', // we have to fix position....
14413 cls: 'modal-content',
14415 Roo.bootstrap.ComboBox.header,
14416 Roo.bootstrap.ComboBox.body,
14417 Roo.bootstrap.ComboBox.footer
14426 * Ext JS Library 1.1.1
14427 * Copyright(c) 2006-2007, Ext JS, LLC.
14429 * Originally Released Under LGPL - original licence link has changed is not relivant.
14432 * <script type="text/javascript">
14437 * @extends Roo.util.Observable
14438 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14439 * This class also supports single and multi selection modes. <br>
14440 * Create a data model bound view:
14442 var store = new Roo.data.Store(...);
14444 var view = new Roo.View({
14446 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14448 singleSelect: true,
14449 selectedClass: "ydataview-selected",
14453 // listen for node click?
14454 view.on("click", function(vw, index, node, e){
14455 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14459 dataModel.load("foobar.xml");
14461 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14463 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14464 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14466 * Note: old style constructor is still suported (container, template, config)
14469 * Create a new View
14470 * @param {Object} config The config object
14473 Roo.View = function(config, depreciated_tpl, depreciated_config){
14475 this.parent = false;
14477 if (typeof(depreciated_tpl) == 'undefined') {
14478 // new way.. - universal constructor.
14479 Roo.apply(this, config);
14480 this.el = Roo.get(this.el);
14483 this.el = Roo.get(config);
14484 this.tpl = depreciated_tpl;
14485 Roo.apply(this, depreciated_config);
14487 this.wrapEl = this.el.wrap().wrap();
14488 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14491 if(typeof(this.tpl) == "string"){
14492 this.tpl = new Roo.Template(this.tpl);
14494 // support xtype ctors..
14495 this.tpl = new Roo.factory(this.tpl, Roo);
14499 this.tpl.compile();
14504 * @event beforeclick
14505 * Fires before a click is processed. Returns false to cancel the default action.
14506 * @param {Roo.View} this
14507 * @param {Number} index The index of the target node
14508 * @param {HTMLElement} node The target node
14509 * @param {Roo.EventObject} e The raw event object
14511 "beforeclick" : true,
14514 * Fires when a template node is clicked.
14515 * @param {Roo.View} this
14516 * @param {Number} index The index of the target node
14517 * @param {HTMLElement} node The target node
14518 * @param {Roo.EventObject} e The raw event object
14523 * Fires when a template node is double clicked.
14524 * @param {Roo.View} this
14525 * @param {Number} index The index of the target node
14526 * @param {HTMLElement} node The target node
14527 * @param {Roo.EventObject} e The raw event object
14531 * @event contextmenu
14532 * Fires when a template node is right clicked.
14533 * @param {Roo.View} this
14534 * @param {Number} index The index of the target node
14535 * @param {HTMLElement} node The target node
14536 * @param {Roo.EventObject} e The raw event object
14538 "contextmenu" : true,
14540 * @event selectionchange
14541 * Fires when the selected nodes change.
14542 * @param {Roo.View} this
14543 * @param {Array} selections Array of the selected nodes
14545 "selectionchange" : true,
14548 * @event beforeselect
14549 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14550 * @param {Roo.View} this
14551 * @param {HTMLElement} node The node to be selected
14552 * @param {Array} selections Array of currently selected nodes
14554 "beforeselect" : true,
14556 * @event preparedata
14557 * Fires on every row to render, to allow you to change the data.
14558 * @param {Roo.View} this
14559 * @param {Object} data to be rendered (change this)
14561 "preparedata" : true
14569 "click": this.onClick,
14570 "dblclick": this.onDblClick,
14571 "contextmenu": this.onContextMenu,
14575 this.selections = [];
14577 this.cmp = new Roo.CompositeElementLite([]);
14579 this.store = Roo.factory(this.store, Roo.data);
14580 this.setStore(this.store, true);
14583 if ( this.footer && this.footer.xtype) {
14585 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14587 this.footer.dataSource = this.store;
14588 this.footer.container = fctr;
14589 this.footer = Roo.factory(this.footer, Roo);
14590 fctr.insertFirst(this.el);
14592 // this is a bit insane - as the paging toolbar seems to detach the el..
14593 // dom.parentNode.parentNode.parentNode
14594 // they get detached?
14598 Roo.View.superclass.constructor.call(this);
14603 Roo.extend(Roo.View, Roo.util.Observable, {
14606 * @cfg {Roo.data.Store} store Data store to load data from.
14611 * @cfg {String|Roo.Element} el The container element.
14616 * @cfg {String|Roo.Template} tpl The template used by this View
14620 * @cfg {String} dataName the named area of the template to use as the data area
14621 * Works with domtemplates roo-name="name"
14625 * @cfg {String} selectedClass The css class to add to selected nodes
14627 selectedClass : "x-view-selected",
14629 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14634 * @cfg {String} text to display on mask (default Loading)
14638 * @cfg {Boolean} multiSelect Allow multiple selection
14640 multiSelect : false,
14642 * @cfg {Boolean} singleSelect Allow single selection
14644 singleSelect: false,
14647 * @cfg {Boolean} toggleSelect - selecting
14649 toggleSelect : false,
14652 * @cfg {Boolean} tickable - selecting
14657 * Returns the element this view is bound to.
14658 * @return {Roo.Element}
14660 getEl : function(){
14661 return this.wrapEl;
14667 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14669 refresh : function(){
14670 //Roo.log('refresh');
14673 // if we are using something like 'domtemplate', then
14674 // the what gets used is:
14675 // t.applySubtemplate(NAME, data, wrapping data..)
14676 // the outer template then get' applied with
14677 // the store 'extra data'
14678 // and the body get's added to the
14679 // roo-name="data" node?
14680 // <span class='roo-tpl-{name}'></span> ?????
14684 this.clearSelections();
14685 this.el.update("");
14687 var records = this.store.getRange();
14688 if(records.length < 1) {
14690 // is this valid?? = should it render a template??
14692 this.el.update(this.emptyText);
14696 if (this.dataName) {
14697 this.el.update(t.apply(this.store.meta)); //????
14698 el = this.el.child('.roo-tpl-' + this.dataName);
14701 for(var i = 0, len = records.length; i < len; i++){
14702 var data = this.prepareData(records[i].data, i, records[i]);
14703 this.fireEvent("preparedata", this, data, i, records[i]);
14705 var d = Roo.apply({}, data);
14708 Roo.apply(d, {'roo-id' : Roo.id()});
14712 Roo.each(this.parent.item, function(item){
14713 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14716 Roo.apply(d, {'roo-data-checked' : 'checked'});
14720 html[html.length] = Roo.util.Format.trim(
14722 t.applySubtemplate(this.dataName, d, this.store.meta) :
14729 el.update(html.join(""));
14730 this.nodes = el.dom.childNodes;
14731 this.updateIndexes(0);
14736 * Function to override to reformat the data that is sent to
14737 * the template for each node.
14738 * DEPRICATED - use the preparedata event handler.
14739 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14740 * a JSON object for an UpdateManager bound view).
14742 prepareData : function(data, index, record)
14744 this.fireEvent("preparedata", this, data, index, record);
14748 onUpdate : function(ds, record){
14749 // Roo.log('on update');
14750 this.clearSelections();
14751 var index = this.store.indexOf(record);
14752 var n = this.nodes[index];
14753 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14754 n.parentNode.removeChild(n);
14755 this.updateIndexes(index, index);
14761 onAdd : function(ds, records, index)
14763 //Roo.log(['on Add', ds, records, index] );
14764 this.clearSelections();
14765 if(this.nodes.length == 0){
14769 var n = this.nodes[index];
14770 for(var i = 0, len = records.length; i < len; i++){
14771 var d = this.prepareData(records[i].data, i, records[i]);
14773 this.tpl.insertBefore(n, d);
14776 this.tpl.append(this.el, d);
14779 this.updateIndexes(index);
14782 onRemove : function(ds, record, index){
14783 // Roo.log('onRemove');
14784 this.clearSelections();
14785 var el = this.dataName ?
14786 this.el.child('.roo-tpl-' + this.dataName) :
14789 el.dom.removeChild(this.nodes[index]);
14790 this.updateIndexes(index);
14794 * Refresh an individual node.
14795 * @param {Number} index
14797 refreshNode : function(index){
14798 this.onUpdate(this.store, this.store.getAt(index));
14801 updateIndexes : function(startIndex, endIndex){
14802 var ns = this.nodes;
14803 startIndex = startIndex || 0;
14804 endIndex = endIndex || ns.length - 1;
14805 for(var i = startIndex; i <= endIndex; i++){
14806 ns[i].nodeIndex = i;
14811 * Changes the data store this view uses and refresh the view.
14812 * @param {Store} store
14814 setStore : function(store, initial){
14815 if(!initial && this.store){
14816 this.store.un("datachanged", this.refresh);
14817 this.store.un("add", this.onAdd);
14818 this.store.un("remove", this.onRemove);
14819 this.store.un("update", this.onUpdate);
14820 this.store.un("clear", this.refresh);
14821 this.store.un("beforeload", this.onBeforeLoad);
14822 this.store.un("load", this.onLoad);
14823 this.store.un("loadexception", this.onLoad);
14827 store.on("datachanged", this.refresh, this);
14828 store.on("add", this.onAdd, this);
14829 store.on("remove", this.onRemove, this);
14830 store.on("update", this.onUpdate, this);
14831 store.on("clear", this.refresh, this);
14832 store.on("beforeload", this.onBeforeLoad, this);
14833 store.on("load", this.onLoad, this);
14834 store.on("loadexception", this.onLoad, this);
14842 * onbeforeLoad - masks the loading area.
14845 onBeforeLoad : function(store,opts)
14847 //Roo.log('onBeforeLoad');
14849 this.el.update("");
14851 this.el.mask(this.mask ? this.mask : "Loading" );
14853 onLoad : function ()
14860 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14861 * @param {HTMLElement} node
14862 * @return {HTMLElement} The template node
14864 findItemFromChild : function(node){
14865 var el = this.dataName ?
14866 this.el.child('.roo-tpl-' + this.dataName,true) :
14869 if(!node || node.parentNode == el){
14872 var p = node.parentNode;
14873 while(p && p != el){
14874 if(p.parentNode == el){
14883 onClick : function(e){
14884 var item = this.findItemFromChild(e.getTarget());
14886 var index = this.indexOf(item);
14887 if(this.onItemClick(item, index, e) !== false){
14888 this.fireEvent("click", this, index, item, e);
14891 this.clearSelections();
14896 onContextMenu : function(e){
14897 var item = this.findItemFromChild(e.getTarget());
14899 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14904 onDblClick : function(e){
14905 var item = this.findItemFromChild(e.getTarget());
14907 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14911 onItemClick : function(item, index, e)
14913 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14916 if (this.toggleSelect) {
14917 var m = this.isSelected(item) ? 'unselect' : 'select';
14920 _t[m](item, true, false);
14923 if(this.multiSelect || this.singleSelect){
14924 if(this.multiSelect && e.shiftKey && this.lastSelection){
14925 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14927 this.select(item, this.multiSelect && e.ctrlKey);
14928 this.lastSelection = item;
14931 if(!this.tickable){
14932 e.preventDefault();
14940 * Get the number of selected nodes.
14943 getSelectionCount : function(){
14944 return this.selections.length;
14948 * Get the currently selected nodes.
14949 * @return {Array} An array of HTMLElements
14951 getSelectedNodes : function(){
14952 return this.selections;
14956 * Get the indexes of the selected nodes.
14959 getSelectedIndexes : function(){
14960 var indexes = [], s = this.selections;
14961 for(var i = 0, len = s.length; i < len; i++){
14962 indexes.push(s[i].nodeIndex);
14968 * Clear all selections
14969 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14971 clearSelections : function(suppressEvent){
14972 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14973 this.cmp.elements = this.selections;
14974 this.cmp.removeClass(this.selectedClass);
14975 this.selections = [];
14976 if(!suppressEvent){
14977 this.fireEvent("selectionchange", this, this.selections);
14983 * Returns true if the passed node is selected
14984 * @param {HTMLElement/Number} node The node or node index
14985 * @return {Boolean}
14987 isSelected : function(node){
14988 var s = this.selections;
14992 node = this.getNode(node);
14993 return s.indexOf(node) !== -1;
14998 * @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
14999 * @param {Boolean} keepExisting (optional) true to keep existing selections
15000 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15002 select : function(nodeInfo, keepExisting, suppressEvent){
15003 if(nodeInfo instanceof Array){
15005 this.clearSelections(true);
15007 for(var i = 0, len = nodeInfo.length; i < len; i++){
15008 this.select(nodeInfo[i], true, true);
15012 var node = this.getNode(nodeInfo);
15013 if(!node || this.isSelected(node)){
15014 return; // already selected.
15017 this.clearSelections(true);
15020 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15021 Roo.fly(node).addClass(this.selectedClass);
15022 this.selections.push(node);
15023 if(!suppressEvent){
15024 this.fireEvent("selectionchange", this, this.selections);
15032 * @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
15033 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15034 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15036 unselect : function(nodeInfo, keepExisting, suppressEvent)
15038 if(nodeInfo instanceof Array){
15039 Roo.each(this.selections, function(s) {
15040 this.unselect(s, nodeInfo);
15044 var node = this.getNode(nodeInfo);
15045 if(!node || !this.isSelected(node)){
15046 //Roo.log("not selected");
15047 return; // not selected.
15051 Roo.each(this.selections, function(s) {
15053 Roo.fly(node).removeClass(this.selectedClass);
15060 this.selections= ns;
15061 this.fireEvent("selectionchange", this, this.selections);
15065 * Gets a template node.
15066 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15067 * @return {HTMLElement} The node or null if it wasn't found
15069 getNode : function(nodeInfo){
15070 if(typeof nodeInfo == "string"){
15071 return document.getElementById(nodeInfo);
15072 }else if(typeof nodeInfo == "number"){
15073 return this.nodes[nodeInfo];
15079 * Gets a range template nodes.
15080 * @param {Number} startIndex
15081 * @param {Number} endIndex
15082 * @return {Array} An array of nodes
15084 getNodes : function(start, end){
15085 var ns = this.nodes;
15086 start = start || 0;
15087 end = typeof end == "undefined" ? ns.length - 1 : end;
15090 for(var i = start; i <= end; i++){
15094 for(var i = start; i >= end; i--){
15102 * Finds the index of the passed node
15103 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15104 * @return {Number} The index of the node or -1
15106 indexOf : function(node){
15107 node = this.getNode(node);
15108 if(typeof node.nodeIndex == "number"){
15109 return node.nodeIndex;
15111 var ns = this.nodes;
15112 for(var i = 0, len = ns.length; i < len; i++){
15123 * based on jquery fullcalendar
15127 Roo.bootstrap = Roo.bootstrap || {};
15129 * @class Roo.bootstrap.Calendar
15130 * @extends Roo.bootstrap.Component
15131 * Bootstrap Calendar class
15132 * @cfg {Boolean} loadMask (true|false) default false
15133 * @cfg {Object} header generate the user specific header of the calendar, default false
15136 * Create a new Container
15137 * @param {Object} config The config object
15142 Roo.bootstrap.Calendar = function(config){
15143 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15147 * Fires when a date is selected
15148 * @param {DatePicker} this
15149 * @param {Date} date The selected date
15153 * @event monthchange
15154 * Fires when the displayed month changes
15155 * @param {DatePicker} this
15156 * @param {Date} date The selected month
15158 'monthchange': true,
15160 * @event evententer
15161 * Fires when mouse over an event
15162 * @param {Calendar} this
15163 * @param {event} Event
15165 'evententer': true,
15167 * @event eventleave
15168 * Fires when the mouse leaves an
15169 * @param {Calendar} this
15172 'eventleave': true,
15174 * @event eventclick
15175 * Fires when the mouse click an
15176 * @param {Calendar} this
15185 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15188 * @cfg {Number} startDay
15189 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15197 getAutoCreate : function(){
15200 var fc_button = function(name, corner, style, content ) {
15201 return Roo.apply({},{
15203 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15205 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15208 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15219 style : 'width:100%',
15226 cls : 'fc-header-left',
15228 fc_button('prev', 'left', 'arrow', '‹' ),
15229 fc_button('next', 'right', 'arrow', '›' ),
15230 { tag: 'span', cls: 'fc-header-space' },
15231 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15239 cls : 'fc-header-center',
15243 cls: 'fc-header-title',
15246 html : 'month / year'
15254 cls : 'fc-header-right',
15256 /* fc_button('month', 'left', '', 'month' ),
15257 fc_button('week', '', '', 'week' ),
15258 fc_button('day', 'right', '', 'day' )
15270 header = this.header;
15273 var cal_heads = function() {
15275 // fixme - handle this.
15277 for (var i =0; i < Date.dayNames.length; i++) {
15278 var d = Date.dayNames[i];
15281 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15282 html : d.substring(0,3)
15286 ret[0].cls += ' fc-first';
15287 ret[6].cls += ' fc-last';
15290 var cal_cell = function(n) {
15293 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15298 cls: 'fc-day-number',
15302 cls: 'fc-day-content',
15306 style: 'position: relative;' // height: 17px;
15318 var cal_rows = function() {
15321 for (var r = 0; r < 6; r++) {
15328 for (var i =0; i < Date.dayNames.length; i++) {
15329 var d = Date.dayNames[i];
15330 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15333 row.cn[0].cls+=' fc-first';
15334 row.cn[0].cn[0].style = 'min-height:90px';
15335 row.cn[6].cls+=' fc-last';
15339 ret[0].cls += ' fc-first';
15340 ret[4].cls += ' fc-prev-last';
15341 ret[5].cls += ' fc-last';
15348 cls: 'fc-border-separate',
15349 style : 'width:100%',
15357 cls : 'fc-first fc-last',
15375 cls : 'fc-content',
15376 style : "position: relative;",
15379 cls : 'fc-view fc-view-month fc-grid',
15380 style : 'position: relative',
15381 unselectable : 'on',
15384 cls : 'fc-event-container',
15385 style : 'position:absolute;z-index:8;top:0;left:0;'
15403 initEvents : function()
15406 throw "can not find store for calendar";
15412 style: "text-align:center",
15416 style: "background-color:white;width:50%;margin:250 auto",
15420 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15431 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15433 var size = this.el.select('.fc-content', true).first().getSize();
15434 this.maskEl.setSize(size.width, size.height);
15435 this.maskEl.enableDisplayMode("block");
15436 if(!this.loadMask){
15437 this.maskEl.hide();
15440 this.store = Roo.factory(this.store, Roo.data);
15441 this.store.on('load', this.onLoad, this);
15442 this.store.on('beforeload', this.onBeforeLoad, this);
15446 this.cells = this.el.select('.fc-day',true);
15447 //Roo.log(this.cells);
15448 this.textNodes = this.el.query('.fc-day-number');
15449 this.cells.addClassOnOver('fc-state-hover');
15451 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15452 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15453 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15454 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15456 this.on('monthchange', this.onMonthChange, this);
15458 this.update(new Date().clearTime());
15461 resize : function() {
15462 var sz = this.el.getSize();
15464 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15465 this.el.select('.fc-day-content div',true).setHeight(34);
15470 showPrevMonth : function(e){
15471 this.update(this.activeDate.add("mo", -1));
15473 showToday : function(e){
15474 this.update(new Date().clearTime());
15477 showNextMonth : function(e){
15478 this.update(this.activeDate.add("mo", 1));
15482 showPrevYear : function(){
15483 this.update(this.activeDate.add("y", -1));
15487 showNextYear : function(){
15488 this.update(this.activeDate.add("y", 1));
15493 update : function(date)
15495 var vd = this.activeDate;
15496 this.activeDate = date;
15497 // if(vd && this.el){
15498 // var t = date.getTime();
15499 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15500 // Roo.log('using add remove');
15502 // this.fireEvent('monthchange', this, date);
15504 // this.cells.removeClass("fc-state-highlight");
15505 // this.cells.each(function(c){
15506 // if(c.dateValue == t){
15507 // c.addClass("fc-state-highlight");
15508 // setTimeout(function(){
15509 // try{c.dom.firstChild.focus();}catch(e){}
15519 var days = date.getDaysInMonth();
15521 var firstOfMonth = date.getFirstDateOfMonth();
15522 var startingPos = firstOfMonth.getDay()-this.startDay;
15524 if(startingPos < this.startDay){
15528 var pm = date.add(Date.MONTH, -1);
15529 var prevStart = pm.getDaysInMonth()-startingPos;
15531 this.cells = this.el.select('.fc-day',true);
15532 this.textNodes = this.el.query('.fc-day-number');
15533 this.cells.addClassOnOver('fc-state-hover');
15535 var cells = this.cells.elements;
15536 var textEls = this.textNodes;
15538 Roo.each(cells, function(cell){
15539 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15542 days += startingPos;
15544 // convert everything to numbers so it's fast
15545 var day = 86400000;
15546 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15549 //Roo.log(prevStart);
15551 var today = new Date().clearTime().getTime();
15552 var sel = date.clearTime().getTime();
15553 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15554 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15555 var ddMatch = this.disabledDatesRE;
15556 var ddText = this.disabledDatesText;
15557 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15558 var ddaysText = this.disabledDaysText;
15559 var format = this.format;
15561 var setCellClass = function(cal, cell){
15565 //Roo.log('set Cell Class');
15567 var t = d.getTime();
15571 cell.dateValue = t;
15573 cell.className += " fc-today";
15574 cell.className += " fc-state-highlight";
15575 cell.title = cal.todayText;
15578 // disable highlight in other month..
15579 //cell.className += " fc-state-highlight";
15584 cell.className = " fc-state-disabled";
15585 cell.title = cal.minText;
15589 cell.className = " fc-state-disabled";
15590 cell.title = cal.maxText;
15594 if(ddays.indexOf(d.getDay()) != -1){
15595 cell.title = ddaysText;
15596 cell.className = " fc-state-disabled";
15599 if(ddMatch && format){
15600 var fvalue = d.dateFormat(format);
15601 if(ddMatch.test(fvalue)){
15602 cell.title = ddText.replace("%0", fvalue);
15603 cell.className = " fc-state-disabled";
15607 if (!cell.initialClassName) {
15608 cell.initialClassName = cell.dom.className;
15611 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15616 for(; i < startingPos; i++) {
15617 textEls[i].innerHTML = (++prevStart);
15618 d.setDate(d.getDate()+1);
15620 cells[i].className = "fc-past fc-other-month";
15621 setCellClass(this, cells[i]);
15626 for(; i < days; i++){
15627 intDay = i - startingPos + 1;
15628 textEls[i].innerHTML = (intDay);
15629 d.setDate(d.getDate()+1);
15631 cells[i].className = ''; // "x-date-active";
15632 setCellClass(this, cells[i]);
15636 for(; i < 42; i++) {
15637 textEls[i].innerHTML = (++extraDays);
15638 d.setDate(d.getDate()+1);
15640 cells[i].className = "fc-future fc-other-month";
15641 setCellClass(this, cells[i]);
15644 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15646 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15648 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15649 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15651 if(totalRows != 6){
15652 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15653 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15656 this.fireEvent('monthchange', this, date);
15660 if(!this.internalRender){
15661 var main = this.el.dom.firstChild;
15662 var w = main.offsetWidth;
15663 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15664 Roo.fly(main).setWidth(w);
15665 this.internalRender = true;
15666 // opera does not respect the auto grow header center column
15667 // then, after it gets a width opera refuses to recalculate
15668 // without a second pass
15669 if(Roo.isOpera && !this.secondPass){
15670 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15671 this.secondPass = true;
15672 this.update.defer(10, this, [date]);
15679 findCell : function(dt) {
15680 dt = dt.clearTime().getTime();
15682 this.cells.each(function(c){
15683 //Roo.log("check " +c.dateValue + '?=' + dt);
15684 if(c.dateValue == dt){
15694 findCells : function(ev) {
15695 var s = ev.start.clone().clearTime().getTime();
15697 var e= ev.end.clone().clearTime().getTime();
15700 this.cells.each(function(c){
15701 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15703 if(c.dateValue > e){
15706 if(c.dateValue < s){
15715 // findBestRow: function(cells)
15719 // for (var i =0 ; i < cells.length;i++) {
15720 // ret = Math.max(cells[i].rows || 0,ret);
15727 addItem : function(ev)
15729 // look for vertical location slot in
15730 var cells = this.findCells(ev);
15732 // ev.row = this.findBestRow(cells);
15734 // work out the location.
15738 for(var i =0; i < cells.length; i++) {
15740 cells[i].row = cells[0].row;
15743 cells[i].row = cells[i].row + 1;
15753 if (crow.start.getY() == cells[i].getY()) {
15755 crow.end = cells[i];
15772 cells[0].events.push(ev);
15774 this.calevents.push(ev);
15777 clearEvents: function() {
15779 if(!this.calevents){
15783 Roo.each(this.cells.elements, function(c){
15789 Roo.each(this.calevents, function(e) {
15790 Roo.each(e.els, function(el) {
15791 el.un('mouseenter' ,this.onEventEnter, this);
15792 el.un('mouseleave' ,this.onEventLeave, this);
15797 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15803 renderEvents: function()
15807 this.cells.each(function(c) {
15816 if(c.row != c.events.length){
15817 r = 4 - (4 - (c.row - c.events.length));
15820 c.events = ev.slice(0, r);
15821 c.more = ev.slice(r);
15823 if(c.more.length && c.more.length == 1){
15824 c.events.push(c.more.pop());
15827 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15831 this.cells.each(function(c) {
15833 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15836 for (var e = 0; e < c.events.length; e++){
15837 var ev = c.events[e];
15838 var rows = ev.rows;
15840 for(var i = 0; i < rows.length; i++) {
15842 // how many rows should it span..
15845 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15846 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15848 unselectable : "on",
15851 cls: 'fc-event-inner',
15855 // cls: 'fc-event-time',
15856 // html : cells.length > 1 ? '' : ev.time
15860 cls: 'fc-event-title',
15861 html : String.format('{0}', ev.title)
15868 cls: 'ui-resizable-handle ui-resizable-e',
15869 html : '  '
15876 cfg.cls += ' fc-event-start';
15878 if ((i+1) == rows.length) {
15879 cfg.cls += ' fc-event-end';
15882 var ctr = _this.el.select('.fc-event-container',true).first();
15883 var cg = ctr.createChild(cfg);
15885 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15886 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15888 var r = (c.more.length) ? 1 : 0;
15889 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15890 cg.setWidth(ebox.right - sbox.x -2);
15892 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15893 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15894 cg.on('click', _this.onEventClick, _this, ev);
15905 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15906 style : 'position: absolute',
15907 unselectable : "on",
15910 cls: 'fc-event-inner',
15914 cls: 'fc-event-title',
15922 cls: 'ui-resizable-handle ui-resizable-e',
15923 html : '  '
15929 var ctr = _this.el.select('.fc-event-container',true).first();
15930 var cg = ctr.createChild(cfg);
15932 var sbox = c.select('.fc-day-content',true).first().getBox();
15933 var ebox = c.select('.fc-day-content',true).first().getBox();
15935 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15936 cg.setWidth(ebox.right - sbox.x -2);
15938 cg.on('click', _this.onMoreEventClick, _this, c.more);
15948 onEventEnter: function (e, el,event,d) {
15949 this.fireEvent('evententer', this, el, event);
15952 onEventLeave: function (e, el,event,d) {
15953 this.fireEvent('eventleave', this, el, event);
15956 onEventClick: function (e, el,event,d) {
15957 this.fireEvent('eventclick', this, el, event);
15960 onMonthChange: function () {
15964 onMoreEventClick: function(e, el, more)
15968 this.calpopover.placement = 'right';
15969 this.calpopover.setTitle('More');
15971 this.calpopover.setContent('');
15973 var ctr = this.calpopover.el.select('.popover-content', true).first();
15975 Roo.each(more, function(m){
15977 cls : 'fc-event-hori fc-event-draggable',
15980 var cg = ctr.createChild(cfg);
15982 cg.on('click', _this.onEventClick, _this, m);
15985 this.calpopover.show(el);
15990 onLoad: function ()
15992 this.calevents = [];
15995 if(this.store.getCount() > 0){
15996 this.store.data.each(function(d){
15999 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16000 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16001 time : d.data.start_time,
16002 title : d.data.title,
16003 description : d.data.description,
16004 venue : d.data.venue
16009 this.renderEvents();
16011 if(this.calevents.length && this.loadMask){
16012 this.maskEl.hide();
16016 onBeforeLoad: function()
16018 this.clearEvents();
16020 this.maskEl.show();
16034 * @class Roo.bootstrap.Popover
16035 * @extends Roo.bootstrap.Component
16036 * Bootstrap Popover class
16037 * @cfg {String} html contents of the popover (or false to use children..)
16038 * @cfg {String} title of popover (or false to hide)
16039 * @cfg {String} placement how it is placed
16040 * @cfg {String} trigger click || hover (or false to trigger manually)
16041 * @cfg {String} over what (parent or false to trigger manually.)
16042 * @cfg {Number} delay - delay before showing
16045 * Create a new Popover
16046 * @param {Object} config The config object
16049 Roo.bootstrap.Popover = function(config){
16050 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16056 * After the popover show
16058 * @param {Roo.bootstrap.Popover} this
16063 * After the popover hide
16065 * @param {Roo.bootstrap.Popover} this
16071 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16073 title: 'Fill in a title',
16076 placement : 'right',
16077 trigger : 'hover', // hover
16083 can_build_overlaid : false,
16085 getChildContainer : function()
16087 return this.el.select('.popover-content',true).first();
16090 getAutoCreate : function(){
16093 cls : 'popover roo-dynamic',
16094 style: 'display:block',
16100 cls : 'popover-inner',
16104 cls: 'popover-title',
16108 cls : 'popover-content',
16119 setTitle: function(str)
16122 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16124 setContent: function(str)
16127 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16129 // as it get's added to the bottom of the page.
16130 onRender : function(ct, position)
16132 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16134 var cfg = Roo.apply({}, this.getAutoCreate());
16138 cfg.cls += ' ' + this.cls;
16141 cfg.style = this.style;
16143 //Roo.log("adding to ");
16144 this.el = Roo.get(document.body).createChild(cfg, position);
16145 // Roo.log(this.el);
16150 initEvents : function()
16152 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16153 this.el.enableDisplayMode('block');
16155 if (this.over === false) {
16158 if (this.triggers === false) {
16161 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16162 var triggers = this.trigger ? this.trigger.split(' ') : [];
16163 Roo.each(triggers, function(trigger) {
16165 if (trigger == 'click') {
16166 on_el.on('click', this.toggle, this);
16167 } else if (trigger != 'manual') {
16168 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16169 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16171 on_el.on(eventIn ,this.enter, this);
16172 on_el.on(eventOut, this.leave, this);
16183 toggle : function () {
16184 this.hoverState == 'in' ? this.leave() : this.enter();
16187 enter : function () {
16189 clearTimeout(this.timeout);
16191 this.hoverState = 'in';
16193 if (!this.delay || !this.delay.show) {
16198 this.timeout = setTimeout(function () {
16199 if (_t.hoverState == 'in') {
16202 }, this.delay.show)
16205 leave : function() {
16206 clearTimeout(this.timeout);
16208 this.hoverState = 'out';
16210 if (!this.delay || !this.delay.hide) {
16215 this.timeout = setTimeout(function () {
16216 if (_t.hoverState == 'out') {
16219 }, this.delay.hide)
16222 show : function (on_el)
16225 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16229 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16230 if (this.html !== false) {
16231 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16233 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16234 if (!this.title.length) {
16235 this.el.select('.popover-title',true).hide();
16238 var placement = typeof this.placement == 'function' ?
16239 this.placement.call(this, this.el, on_el) :
16242 var autoToken = /\s?auto?\s?/i;
16243 var autoPlace = autoToken.test(placement);
16245 placement = placement.replace(autoToken, '') || 'top';
16249 //this.el.setXY([0,0]);
16251 this.el.dom.style.display='block';
16252 this.el.addClass(placement);
16254 //this.el.appendTo(on_el);
16256 var p = this.getPosition();
16257 var box = this.el.getBox();
16262 var align = Roo.bootstrap.Popover.alignment[placement];
16263 this.el.alignTo(on_el, align[0],align[1]);
16264 //var arrow = this.el.select('.arrow',true).first();
16265 //arrow.set(align[2],
16267 this.el.addClass('in');
16270 if (this.el.hasClass('fade')) {
16274 this.hoverState = 'in';
16276 this.fireEvent('show', this);
16281 this.el.setXY([0,0]);
16282 this.el.removeClass('in');
16284 this.hoverState = null;
16286 this.fireEvent('hide', this);
16291 Roo.bootstrap.Popover.alignment = {
16292 'left' : ['r-l', [-10,0], 'right'],
16293 'right' : ['l-r', [10,0], 'left'],
16294 'bottom' : ['t-b', [0,10], 'top'],
16295 'top' : [ 'b-t', [0,-10], 'bottom']
16306 * @class Roo.bootstrap.Progress
16307 * @extends Roo.bootstrap.Component
16308 * Bootstrap Progress class
16309 * @cfg {Boolean} striped striped of the progress bar
16310 * @cfg {Boolean} active animated of the progress bar
16314 * Create a new Progress
16315 * @param {Object} config The config object
16318 Roo.bootstrap.Progress = function(config){
16319 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16322 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16327 getAutoCreate : function(){
16335 cfg.cls += ' progress-striped';
16339 cfg.cls += ' active';
16358 * @class Roo.bootstrap.ProgressBar
16359 * @extends Roo.bootstrap.Component
16360 * Bootstrap ProgressBar class
16361 * @cfg {Number} aria_valuenow aria-value now
16362 * @cfg {Number} aria_valuemin aria-value min
16363 * @cfg {Number} aria_valuemax aria-value max
16364 * @cfg {String} label label for the progress bar
16365 * @cfg {String} panel (success | info | warning | danger )
16366 * @cfg {String} role role of the progress bar
16367 * @cfg {String} sr_only text
16371 * Create a new ProgressBar
16372 * @param {Object} config The config object
16375 Roo.bootstrap.ProgressBar = function(config){
16376 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16379 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16383 aria_valuemax : 100,
16389 getAutoCreate : function()
16394 cls: 'progress-bar',
16395 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16407 cfg.role = this.role;
16410 if(this.aria_valuenow){
16411 cfg['aria-valuenow'] = this.aria_valuenow;
16414 if(this.aria_valuemin){
16415 cfg['aria-valuemin'] = this.aria_valuemin;
16418 if(this.aria_valuemax){
16419 cfg['aria-valuemax'] = this.aria_valuemax;
16422 if(this.label && !this.sr_only){
16423 cfg.html = this.label;
16427 cfg.cls += ' progress-bar-' + this.panel;
16433 update : function(aria_valuenow)
16435 this.aria_valuenow = aria_valuenow;
16437 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16452 * @class Roo.bootstrap.TabGroup
16453 * @extends Roo.bootstrap.Column
16454 * Bootstrap Column class
16455 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16456 * @cfg {Boolean} carousel true to make the group behave like a carousel
16457 * @cfg {Boolean} bullets show bullets for the panels
16458 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16459 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16460 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16461 * @cfg {Boolean} showarrow (true|false) show arrow default true
16464 * Create a new TabGroup
16465 * @param {Object} config The config object
16468 Roo.bootstrap.TabGroup = function(config){
16469 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16471 this.navId = Roo.id();
16474 Roo.bootstrap.TabGroup.register(this);
16478 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16481 transition : false,
16486 slideOnTouch : false,
16489 getAutoCreate : function()
16491 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16493 cfg.cls += ' tab-content';
16495 if (this.carousel) {
16496 cfg.cls += ' carousel slide';
16499 cls : 'carousel-inner',
16503 if(this.bullets && !Roo.isTouch){
16506 cls : 'carousel-bullets',
16510 if(this.bullets_cls){
16511 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16518 cfg.cn[0].cn.push(bullets);
16521 if(this.showarrow){
16522 cfg.cn[0].cn.push({
16524 class : 'carousel-arrow',
16528 class : 'carousel-prev',
16532 class : 'fa fa-chevron-left'
16538 class : 'carousel-next',
16542 class : 'fa fa-chevron-right'
16555 initEvents: function()
16557 if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16558 this.el.on("touchstart", this.onTouchStart, this);
16561 if(this.autoslide){
16564 this.slideFn = window.setInterval(function() {
16565 _this.showPanelNext();
16569 if(this.showarrow){
16570 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16571 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16577 onTouchStart : function(e, el, o)
16579 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16583 this.showPanelNext();
16586 getChildContainer : function()
16588 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16592 * register a Navigation item
16593 * @param {Roo.bootstrap.NavItem} the navitem to add
16595 register : function(item)
16597 this.tabs.push( item);
16598 item.navId = this.navId; // not really needed..
16603 getActivePanel : function()
16606 Roo.each(this.tabs, function(t) {
16616 getPanelByName : function(n)
16619 Roo.each(this.tabs, function(t) {
16620 if (t.tabId == n) {
16628 indexOfPanel : function(p)
16631 Roo.each(this.tabs, function(t,i) {
16632 if (t.tabId == p.tabId) {
16641 * show a specific panel
16642 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16643 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16645 showPanel : function (pan)
16647 if(this.transition || typeof(pan) == 'undefined'){
16648 Roo.log("waiting for the transitionend");
16652 if (typeof(pan) == 'number') {
16653 pan = this.tabs[pan];
16656 if (typeof(pan) == 'string') {
16657 pan = this.getPanelByName(pan);
16660 var cur = this.getActivePanel();
16663 Roo.log('pan or acitve pan is undefined');
16667 if (pan.tabId == this.getActivePanel().tabId) {
16671 if (false === cur.fireEvent('beforedeactivate')) {
16675 if(this.bullets > 0 && !Roo.isTouch){
16676 this.setActiveBullet(this.indexOfPanel(pan));
16679 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16681 this.transition = true;
16682 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16683 var lr = dir == 'next' ? 'left' : 'right';
16684 pan.el.addClass(dir); // or prev
16685 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16686 cur.el.addClass(lr); // or right
16687 pan.el.addClass(lr);
16690 cur.el.on('transitionend', function() {
16691 Roo.log("trans end?");
16693 pan.el.removeClass([lr,dir]);
16694 pan.setActive(true);
16696 cur.el.removeClass([lr]);
16697 cur.setActive(false);
16699 _this.transition = false;
16701 }, this, { single: true } );
16706 cur.setActive(false);
16707 pan.setActive(true);
16712 showPanelNext : function()
16714 var i = this.indexOfPanel(this.getActivePanel());
16716 if (i >= this.tabs.length - 1 && !this.autoslide) {
16720 if (i >= this.tabs.length - 1 && this.autoslide) {
16724 this.showPanel(this.tabs[i+1]);
16727 showPanelPrev : function()
16729 var i = this.indexOfPanel(this.getActivePanel());
16731 if (i < 1 && !this.autoslide) {
16735 if (i < 1 && this.autoslide) {
16736 i = this.tabs.length;
16739 this.showPanel(this.tabs[i-1]);
16743 addBullet: function()
16745 if(!this.bullets || Roo.isTouch){
16748 var ctr = this.el.select('.carousel-bullets',true).first();
16749 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16750 var bullet = ctr.createChild({
16751 cls : 'bullet bullet-' + i
16752 },ctr.dom.lastChild);
16757 bullet.on('click', (function(e, el, o, ii, t){
16759 e.preventDefault();
16761 this.showPanel(ii);
16763 if(this.autoslide && this.slideFn){
16764 clearInterval(this.slideFn);
16765 this.slideFn = window.setInterval(function() {
16766 _this.showPanelNext();
16770 }).createDelegate(this, [i, bullet], true));
16775 setActiveBullet : function(i)
16781 Roo.each(this.el.select('.bullet', true).elements, function(el){
16782 el.removeClass('selected');
16785 var bullet = this.el.select('.bullet-' + i, true).first();
16791 bullet.addClass('selected');
16802 Roo.apply(Roo.bootstrap.TabGroup, {
16806 * register a Navigation Group
16807 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16809 register : function(navgrp)
16811 this.groups[navgrp.navId] = navgrp;
16815 * fetch a Navigation Group based on the navigation ID
16816 * if one does not exist , it will get created.
16817 * @param {string} the navgroup to add
16818 * @returns {Roo.bootstrap.NavGroup} the navgroup
16820 get: function(navId) {
16821 if (typeof(this.groups[navId]) == 'undefined') {
16822 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16824 return this.groups[navId] ;
16839 * @class Roo.bootstrap.TabPanel
16840 * @extends Roo.bootstrap.Component
16841 * Bootstrap TabPanel class
16842 * @cfg {Boolean} active panel active
16843 * @cfg {String} html panel content
16844 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16845 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16846 * @cfg {String} href click to link..
16850 * Create a new TabPanel
16851 * @param {Object} config The config object
16854 Roo.bootstrap.TabPanel = function(config){
16855 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16859 * Fires when the active status changes
16860 * @param {Roo.bootstrap.TabPanel} this
16861 * @param {Boolean} state the new state
16866 * @event beforedeactivate
16867 * Fires before a tab is de-activated - can be used to do validation on a form.
16868 * @param {Roo.bootstrap.TabPanel} this
16869 * @return {Boolean} false if there is an error
16872 'beforedeactivate': true
16875 this.tabId = this.tabId || Roo.id();
16879 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16887 getAutoCreate : function(){
16890 // item is needed for carousel - not sure if it has any effect otherwise
16891 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
16892 html: this.html || ''
16896 cfg.cls += ' active';
16900 cfg.tabId = this.tabId;
16907 initEvents: function()
16909 var p = this.parent();
16910 this.navId = this.navId || p.navId;
16912 if (typeof(this.navId) != 'undefined') {
16913 // not really needed.. but just in case.. parent should be a NavGroup.
16914 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16918 var i = tg.tabs.length - 1;
16920 if(this.active && tg.bullets > 0 && i < tg.bullets){
16921 tg.setActiveBullet(i);
16925 if(this.href.length){
16926 this.el.on('click', this.onClick, this);
16931 onRender : function(ct, position)
16933 // Roo.log("Call onRender: " + this.xtype);
16935 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16943 setActive: function(state)
16945 Roo.log("panel - set active " + this.tabId + "=" + state);
16947 this.active = state;
16949 this.el.removeClass('active');
16951 } else if (!this.el.hasClass('active')) {
16952 this.el.addClass('active');
16955 this.fireEvent('changed', this, state);
16958 onClick: function(e)
16960 e.preventDefault();
16962 window.location.href = this.href;
16979 * @class Roo.bootstrap.DateField
16980 * @extends Roo.bootstrap.Input
16981 * Bootstrap DateField class
16982 * @cfg {Number} weekStart default 0
16983 * @cfg {String} viewMode default empty, (months|years)
16984 * @cfg {String} minViewMode default empty, (months|years)
16985 * @cfg {Number} startDate default -Infinity
16986 * @cfg {Number} endDate default Infinity
16987 * @cfg {Boolean} todayHighlight default false
16988 * @cfg {Boolean} todayBtn default false
16989 * @cfg {Boolean} calendarWeeks default false
16990 * @cfg {Object} daysOfWeekDisabled default empty
16991 * @cfg {Boolean} singleMode default false (true | false)
16993 * @cfg {Boolean} keyboardNavigation default true
16994 * @cfg {String} language default en
16997 * Create a new DateField
16998 * @param {Object} config The config object
17001 Roo.bootstrap.DateField = function(config){
17002 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17006 * Fires when this field show.
17007 * @param {Roo.bootstrap.DateField} this
17008 * @param {Mixed} date The date value
17013 * Fires when this field hide.
17014 * @param {Roo.bootstrap.DateField} this
17015 * @param {Mixed} date The date value
17020 * Fires when select a date.
17021 * @param {Roo.bootstrap.DateField} this
17022 * @param {Mixed} date The date value
17026 * @event beforeselect
17027 * Fires when before select a date.
17028 * @param {Roo.bootstrap.DateField} this
17029 * @param {Mixed} date The date value
17031 beforeselect : true
17035 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
17038 * @cfg {String} format
17039 * The default date format string which can be overriden for localization support. The format must be
17040 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17044 * @cfg {String} altFormats
17045 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17046 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17048 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17056 todayHighlight : false,
17062 keyboardNavigation: true,
17064 calendarWeeks: false,
17066 startDate: -Infinity,
17070 daysOfWeekDisabled: [],
17074 singleMode : false,
17076 UTCDate: function()
17078 return new Date(Date.UTC.apply(Date, arguments));
17081 UTCToday: function()
17083 var today = new Date();
17084 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17087 getDate: function() {
17088 var d = this.getUTCDate();
17089 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17092 getUTCDate: function() {
17096 setDate: function(d) {
17097 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17100 setUTCDate: function(d) {
17102 this.setValue(this.formatDate(this.date));
17105 onRender: function(ct, position)
17108 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17110 this.language = this.language || 'en';
17111 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17112 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17114 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17115 this.format = this.format || 'm/d/y';
17116 this.isInline = false;
17117 this.isInput = true;
17118 this.component = this.el.select('.add-on', true).first() || false;
17119 this.component = (this.component && this.component.length === 0) ? false : this.component;
17120 this.hasInput = this.component && this.inputEL().length;
17122 if (typeof(this.minViewMode === 'string')) {
17123 switch (this.minViewMode) {
17125 this.minViewMode = 1;
17128 this.minViewMode = 2;
17131 this.minViewMode = 0;
17136 if (typeof(this.viewMode === 'string')) {
17137 switch (this.viewMode) {
17150 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17152 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17154 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17156 this.picker().on('mousedown', this.onMousedown, this);
17157 this.picker().on('click', this.onClick, this);
17159 this.picker().addClass('datepicker-dropdown');
17161 this.startViewMode = this.viewMode;
17163 if(this.singleMode){
17164 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17165 v.setVisibilityMode(Roo.Element.DISPLAY);
17169 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17170 v.setStyle('width', '189px');
17174 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17175 if(!this.calendarWeeks){
17180 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17181 v.attr('colspan', function(i, val){
17182 return parseInt(val) + 1;
17187 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17189 this.setStartDate(this.startDate);
17190 this.setEndDate(this.endDate);
17192 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17199 if(this.isInline) {
17204 picker : function()
17206 return this.pickerEl;
17207 // return this.el.select('.datepicker', true).first();
17210 fillDow: function()
17212 var dowCnt = this.weekStart;
17221 if(this.calendarWeeks){
17229 while (dowCnt < this.weekStart + 7) {
17233 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17237 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17240 fillMonths: function()
17243 var months = this.picker().select('>.datepicker-months td', true).first();
17245 months.dom.innerHTML = '';
17251 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17254 months.createChild(month);
17261 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;
17263 if (this.date < this.startDate) {
17264 this.viewDate = new Date(this.startDate);
17265 } else if (this.date > this.endDate) {
17266 this.viewDate = new Date(this.endDate);
17268 this.viewDate = new Date(this.date);
17276 var d = new Date(this.viewDate),
17277 year = d.getUTCFullYear(),
17278 month = d.getUTCMonth(),
17279 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17280 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17281 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17282 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17283 currentDate = this.date && this.date.valueOf(),
17284 today = this.UTCToday();
17286 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17288 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17290 // this.picker.select('>tfoot th.today').
17291 // .text(dates[this.language].today)
17292 // .toggle(this.todayBtn !== false);
17294 this.updateNavArrows();
17297 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17299 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17301 prevMonth.setUTCDate(day);
17303 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17305 var nextMonth = new Date(prevMonth);
17307 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17309 nextMonth = nextMonth.valueOf();
17311 var fillMonths = false;
17313 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17315 while(prevMonth.valueOf() < nextMonth) {
17318 if (prevMonth.getUTCDay() === this.weekStart) {
17320 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17328 if(this.calendarWeeks){
17329 // ISO 8601: First week contains first thursday.
17330 // ISO also states week starts on Monday, but we can be more abstract here.
17332 // Start of current week: based on weekstart/current date
17333 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17334 // Thursday of this week
17335 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17336 // First Thursday of year, year from thursday
17337 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17338 // Calendar week: ms between thursdays, div ms per day, div 7 days
17339 calWeek = (th - yth) / 864e5 / 7 + 1;
17341 fillMonths.cn.push({
17349 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17351 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17354 if (this.todayHighlight &&
17355 prevMonth.getUTCFullYear() == today.getFullYear() &&
17356 prevMonth.getUTCMonth() == today.getMonth() &&
17357 prevMonth.getUTCDate() == today.getDate()) {
17358 clsName += ' today';
17361 if (currentDate && prevMonth.valueOf() === currentDate) {
17362 clsName += ' active';
17365 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17366 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17367 clsName += ' disabled';
17370 fillMonths.cn.push({
17372 cls: 'day ' + clsName,
17373 html: prevMonth.getDate()
17376 prevMonth.setDate(prevMonth.getDate()+1);
17379 var currentYear = this.date && this.date.getUTCFullYear();
17380 var currentMonth = this.date && this.date.getUTCMonth();
17382 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17384 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17385 v.removeClass('active');
17387 if(currentYear === year && k === currentMonth){
17388 v.addClass('active');
17391 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17392 v.addClass('disabled');
17398 year = parseInt(year/10, 10) * 10;
17400 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17402 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17405 for (var i = -1; i < 11; i++) {
17406 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17408 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17416 showMode: function(dir)
17419 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17422 Roo.each(this.picker().select('>div',true).elements, function(v){
17423 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17426 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17431 if(this.isInline) {
17435 this.picker().removeClass(['bottom', 'top']);
17437 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17439 * place to the top of element!
17443 this.picker().addClass('top');
17444 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17449 this.picker().addClass('bottom');
17451 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17454 parseDate : function(value)
17456 if(!value || value instanceof Date){
17459 var v = Date.parseDate(value, this.format);
17460 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17461 v = Date.parseDate(value, 'Y-m-d');
17463 if(!v && this.altFormats){
17464 if(!this.altFormatsArray){
17465 this.altFormatsArray = this.altFormats.split("|");
17467 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17468 v = Date.parseDate(value, this.altFormatsArray[i]);
17474 formatDate : function(date, fmt)
17476 return (!date || !(date instanceof Date)) ?
17477 date : date.dateFormat(fmt || this.format);
17480 onFocus : function()
17482 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17486 onBlur : function()
17488 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17490 var d = this.inputEl().getValue();
17499 this.picker().show();
17503 this.fireEvent('show', this, this.date);
17508 if(this.isInline) {
17511 this.picker().hide();
17512 this.viewMode = this.startViewMode;
17515 this.fireEvent('hide', this, this.date);
17519 onMousedown: function(e)
17521 e.stopPropagation();
17522 e.preventDefault();
17527 Roo.bootstrap.DateField.superclass.keyup.call(this);
17531 setValue: function(v)
17533 if(this.fireEvent('beforeselect', this, v) !== false){
17534 var d = new Date(this.parseDate(v) ).clearTime();
17536 if(isNaN(d.getTime())){
17537 this.date = this.viewDate = '';
17538 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17542 v = this.formatDate(d);
17544 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17546 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17550 this.fireEvent('select', this, this.date);
17554 getValue: function()
17556 return this.formatDate(this.date);
17559 fireKey: function(e)
17561 if (!this.picker().isVisible()){
17562 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17568 var dateChanged = false,
17570 newDate, newViewDate;
17575 e.preventDefault();
17579 if (!this.keyboardNavigation) {
17582 dir = e.keyCode == 37 ? -1 : 1;
17585 newDate = this.moveYear(this.date, dir);
17586 newViewDate = this.moveYear(this.viewDate, dir);
17587 } else if (e.shiftKey){
17588 newDate = this.moveMonth(this.date, dir);
17589 newViewDate = this.moveMonth(this.viewDate, dir);
17591 newDate = new Date(this.date);
17592 newDate.setUTCDate(this.date.getUTCDate() + dir);
17593 newViewDate = new Date(this.viewDate);
17594 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17596 if (this.dateWithinRange(newDate)){
17597 this.date = newDate;
17598 this.viewDate = newViewDate;
17599 this.setValue(this.formatDate(this.date));
17601 e.preventDefault();
17602 dateChanged = true;
17607 if (!this.keyboardNavigation) {
17610 dir = e.keyCode == 38 ? -1 : 1;
17612 newDate = this.moveYear(this.date, dir);
17613 newViewDate = this.moveYear(this.viewDate, dir);
17614 } else if (e.shiftKey){
17615 newDate = this.moveMonth(this.date, dir);
17616 newViewDate = this.moveMonth(this.viewDate, dir);
17618 newDate = new Date(this.date);
17619 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17620 newViewDate = new Date(this.viewDate);
17621 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17623 if (this.dateWithinRange(newDate)){
17624 this.date = newDate;
17625 this.viewDate = newViewDate;
17626 this.setValue(this.formatDate(this.date));
17628 e.preventDefault();
17629 dateChanged = true;
17633 this.setValue(this.formatDate(this.date));
17635 e.preventDefault();
17638 this.setValue(this.formatDate(this.date));
17652 onClick: function(e)
17654 e.stopPropagation();
17655 e.preventDefault();
17657 var target = e.getTarget();
17659 if(target.nodeName.toLowerCase() === 'i'){
17660 target = Roo.get(target).dom.parentNode;
17663 var nodeName = target.nodeName;
17664 var className = target.className;
17665 var html = target.innerHTML;
17666 //Roo.log(nodeName);
17668 switch(nodeName.toLowerCase()) {
17670 switch(className) {
17676 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17677 switch(this.viewMode){
17679 this.viewDate = this.moveMonth(this.viewDate, dir);
17683 this.viewDate = this.moveYear(this.viewDate, dir);
17689 var date = new Date();
17690 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17692 this.setValue(this.formatDate(this.date));
17699 if (className.indexOf('disabled') < 0) {
17700 this.viewDate.setUTCDate(1);
17701 if (className.indexOf('month') > -1) {
17702 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17704 var year = parseInt(html, 10) || 0;
17705 this.viewDate.setUTCFullYear(year);
17709 if(this.singleMode){
17710 this.setValue(this.formatDate(this.viewDate));
17721 //Roo.log(className);
17722 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17723 var day = parseInt(html, 10) || 1;
17724 var year = this.viewDate.getUTCFullYear(),
17725 month = this.viewDate.getUTCMonth();
17727 if (className.indexOf('old') > -1) {
17734 } else if (className.indexOf('new') > -1) {
17742 //Roo.log([year,month,day]);
17743 this.date = this.UTCDate(year, month, day,0,0,0,0);
17744 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17746 //Roo.log(this.formatDate(this.date));
17747 this.setValue(this.formatDate(this.date));
17754 setStartDate: function(startDate)
17756 this.startDate = startDate || -Infinity;
17757 if (this.startDate !== -Infinity) {
17758 this.startDate = this.parseDate(this.startDate);
17761 this.updateNavArrows();
17764 setEndDate: function(endDate)
17766 this.endDate = endDate || Infinity;
17767 if (this.endDate !== Infinity) {
17768 this.endDate = this.parseDate(this.endDate);
17771 this.updateNavArrows();
17774 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17776 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17777 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17778 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17780 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17781 return parseInt(d, 10);
17784 this.updateNavArrows();
17787 updateNavArrows: function()
17789 if(this.singleMode){
17793 var d = new Date(this.viewDate),
17794 year = d.getUTCFullYear(),
17795 month = d.getUTCMonth();
17797 Roo.each(this.picker().select('.prev', true).elements, function(v){
17799 switch (this.viewMode) {
17802 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17808 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17815 Roo.each(this.picker().select('.next', true).elements, function(v){
17817 switch (this.viewMode) {
17820 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17826 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17834 moveMonth: function(date, dir)
17839 var new_date = new Date(date.valueOf()),
17840 day = new_date.getUTCDate(),
17841 month = new_date.getUTCMonth(),
17842 mag = Math.abs(dir),
17844 dir = dir > 0 ? 1 : -1;
17847 // If going back one month, make sure month is not current month
17848 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17850 return new_date.getUTCMonth() == month;
17852 // If going forward one month, make sure month is as expected
17853 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17855 return new_date.getUTCMonth() != new_month;
17857 new_month = month + dir;
17858 new_date.setUTCMonth(new_month);
17859 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17860 if (new_month < 0 || new_month > 11) {
17861 new_month = (new_month + 12) % 12;
17864 // For magnitudes >1, move one month at a time...
17865 for (var i=0; i<mag; i++) {
17866 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17867 new_date = this.moveMonth(new_date, dir);
17869 // ...then reset the day, keeping it in the new month
17870 new_month = new_date.getUTCMonth();
17871 new_date.setUTCDate(day);
17873 return new_month != new_date.getUTCMonth();
17876 // Common date-resetting loop -- if date is beyond end of month, make it
17879 new_date.setUTCDate(--day);
17880 new_date.setUTCMonth(new_month);
17885 moveYear: function(date, dir)
17887 return this.moveMonth(date, dir*12);
17890 dateWithinRange: function(date)
17892 return date >= this.startDate && date <= this.endDate;
17898 this.picker().remove();
17903 Roo.apply(Roo.bootstrap.DateField, {
17914 html: '<i class="fa fa-arrow-left"/>'
17924 html: '<i class="fa fa-arrow-right"/>'
17966 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17967 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17968 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17969 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17970 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17983 navFnc: 'FullYear',
17988 navFnc: 'FullYear',
17993 Roo.apply(Roo.bootstrap.DateField, {
17997 cls: 'datepicker dropdown-menu roo-dynamic',
18001 cls: 'datepicker-days',
18005 cls: 'table-condensed',
18007 Roo.bootstrap.DateField.head,
18011 Roo.bootstrap.DateField.footer
18018 cls: 'datepicker-months',
18022 cls: 'table-condensed',
18024 Roo.bootstrap.DateField.head,
18025 Roo.bootstrap.DateField.content,
18026 Roo.bootstrap.DateField.footer
18033 cls: 'datepicker-years',
18037 cls: 'table-condensed',
18039 Roo.bootstrap.DateField.head,
18040 Roo.bootstrap.DateField.content,
18041 Roo.bootstrap.DateField.footer
18060 * @class Roo.bootstrap.TimeField
18061 * @extends Roo.bootstrap.Input
18062 * Bootstrap DateField class
18066 * Create a new TimeField
18067 * @param {Object} config The config object
18070 Roo.bootstrap.TimeField = function(config){
18071 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18075 * Fires when this field show.
18076 * @param {Roo.bootstrap.DateField} thisthis
18077 * @param {Mixed} date The date value
18082 * Fires when this field hide.
18083 * @param {Roo.bootstrap.DateField} this
18084 * @param {Mixed} date The date value
18089 * Fires when select a date.
18090 * @param {Roo.bootstrap.DateField} this
18091 * @param {Mixed} date The date value
18097 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18100 * @cfg {String} format
18101 * The default time format string which can be overriden for localization support. The format must be
18102 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18106 onRender: function(ct, position)
18109 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18111 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18113 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18115 this.pop = this.picker().select('>.datepicker-time',true).first();
18116 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18118 this.picker().on('mousedown', this.onMousedown, this);
18119 this.picker().on('click', this.onClick, this);
18121 this.picker().addClass('datepicker-dropdown');
18126 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18127 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18128 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18129 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18130 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18131 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18135 fireKey: function(e){
18136 if (!this.picker().isVisible()){
18137 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18143 e.preventDefault();
18151 this.onTogglePeriod();
18154 this.onIncrementMinutes();
18157 this.onDecrementMinutes();
18166 onClick: function(e) {
18167 e.stopPropagation();
18168 e.preventDefault();
18171 picker : function()
18173 return this.el.select('.datepicker', true).first();
18176 fillTime: function()
18178 var time = this.pop.select('tbody', true).first();
18180 time.dom.innerHTML = '';
18195 cls: 'hours-up glyphicon glyphicon-chevron-up'
18215 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18236 cls: 'timepicker-hour',
18251 cls: 'timepicker-minute',
18266 cls: 'btn btn-primary period',
18288 cls: 'hours-down glyphicon glyphicon-chevron-down'
18308 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18326 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18333 var hours = this.time.getHours();
18334 var minutes = this.time.getMinutes();
18347 hours = hours - 12;
18351 hours = '0' + hours;
18355 minutes = '0' + minutes;
18358 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18359 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18360 this.pop.select('button', true).first().dom.innerHTML = period;
18366 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18368 var cls = ['bottom'];
18370 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18377 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18382 this.picker().addClass(cls.join('-'));
18386 Roo.each(cls, function(c){
18388 _this.picker().setTop(_this.inputEl().getHeight());
18392 _this.picker().setTop(0 - _this.picker().getHeight());
18397 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18401 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18408 onFocus : function()
18410 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18414 onBlur : function()
18416 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18422 this.picker().show();
18427 this.fireEvent('show', this, this.date);
18432 this.picker().hide();
18435 this.fireEvent('hide', this, this.date);
18438 setTime : function()
18441 this.setValue(this.time.format(this.format));
18443 this.fireEvent('select', this, this.date);
18448 onMousedown: function(e){
18449 e.stopPropagation();
18450 e.preventDefault();
18453 onIncrementHours: function()
18455 Roo.log('onIncrementHours');
18456 this.time = this.time.add(Date.HOUR, 1);
18461 onDecrementHours: function()
18463 Roo.log('onDecrementHours');
18464 this.time = this.time.add(Date.HOUR, -1);
18468 onIncrementMinutes: function()
18470 Roo.log('onIncrementMinutes');
18471 this.time = this.time.add(Date.MINUTE, 1);
18475 onDecrementMinutes: function()
18477 Roo.log('onDecrementMinutes');
18478 this.time = this.time.add(Date.MINUTE, -1);
18482 onTogglePeriod: function()
18484 Roo.log('onTogglePeriod');
18485 this.time = this.time.add(Date.HOUR, 12);
18492 Roo.apply(Roo.bootstrap.TimeField, {
18522 cls: 'btn btn-info ok',
18534 Roo.apply(Roo.bootstrap.TimeField, {
18538 cls: 'datepicker dropdown-menu',
18542 cls: 'datepicker-time',
18546 cls: 'table-condensed',
18548 Roo.bootstrap.TimeField.content,
18549 Roo.bootstrap.TimeField.footer
18568 * @class Roo.bootstrap.MonthField
18569 * @extends Roo.bootstrap.Input
18570 * Bootstrap MonthField class
18572 * @cfg {String} language default en
18575 * Create a new MonthField
18576 * @param {Object} config The config object
18579 Roo.bootstrap.MonthField = function(config){
18580 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18585 * Fires when this field show.
18586 * @param {Roo.bootstrap.MonthField} this
18587 * @param {Mixed} date The date value
18592 * Fires when this field hide.
18593 * @param {Roo.bootstrap.MonthField} this
18594 * @param {Mixed} date The date value
18599 * Fires when select a date.
18600 * @param {Roo.bootstrap.MonthField} this
18601 * @param {String} oldvalue The old value
18602 * @param {String} newvalue The new value
18608 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18610 onRender: function(ct, position)
18613 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18615 this.language = this.language || 'en';
18616 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18617 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18619 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18620 this.isInline = false;
18621 this.isInput = true;
18622 this.component = this.el.select('.add-on', true).first() || false;
18623 this.component = (this.component && this.component.length === 0) ? false : this.component;
18624 this.hasInput = this.component && this.inputEL().length;
18626 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18628 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18630 this.picker().on('mousedown', this.onMousedown, this);
18631 this.picker().on('click', this.onClick, this);
18633 this.picker().addClass('datepicker-dropdown');
18635 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18636 v.setStyle('width', '189px');
18643 if(this.isInline) {
18649 setValue: function(v, suppressEvent)
18651 var o = this.getValue();
18653 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18657 if(suppressEvent !== true){
18658 this.fireEvent('select', this, o, v);
18663 getValue: function()
18668 onClick: function(e)
18670 e.stopPropagation();
18671 e.preventDefault();
18673 var target = e.getTarget();
18675 if(target.nodeName.toLowerCase() === 'i'){
18676 target = Roo.get(target).dom.parentNode;
18679 var nodeName = target.nodeName;
18680 var className = target.className;
18681 var html = target.innerHTML;
18683 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18687 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18689 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18695 picker : function()
18697 return this.pickerEl;
18700 fillMonths: function()
18703 var months = this.picker().select('>.datepicker-months td', true).first();
18705 months.dom.innerHTML = '';
18711 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18714 months.createChild(month);
18723 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18724 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18727 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18728 e.removeClass('active');
18730 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18731 e.addClass('active');
18738 if(this.isInline) {
18742 this.picker().removeClass(['bottom', 'top']);
18744 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18746 * place to the top of element!
18750 this.picker().addClass('top');
18751 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18756 this.picker().addClass('bottom');
18758 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18761 onFocus : function()
18763 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18767 onBlur : function()
18769 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18771 var d = this.inputEl().getValue();
18780 this.picker().show();
18781 this.picker().select('>.datepicker-months', true).first().show();
18785 this.fireEvent('show', this, this.date);
18790 if(this.isInline) {
18793 this.picker().hide();
18794 this.fireEvent('hide', this, this.date);
18798 onMousedown: function(e)
18800 e.stopPropagation();
18801 e.preventDefault();
18806 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18810 fireKey: function(e)
18812 if (!this.picker().isVisible()){
18813 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18824 e.preventDefault();
18828 dir = e.keyCode == 37 ? -1 : 1;
18830 this.vIndex = this.vIndex + dir;
18832 if(this.vIndex < 0){
18836 if(this.vIndex > 11){
18840 if(isNaN(this.vIndex)){
18844 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18850 dir = e.keyCode == 38 ? -1 : 1;
18852 this.vIndex = this.vIndex + dir * 4;
18854 if(this.vIndex < 0){
18858 if(this.vIndex > 11){
18862 if(isNaN(this.vIndex)){
18866 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18871 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18872 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18876 e.preventDefault();
18879 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18880 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18896 this.picker().remove();
18901 Roo.apply(Roo.bootstrap.MonthField, {
18920 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18921 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18926 Roo.apply(Roo.bootstrap.MonthField, {
18930 cls: 'datepicker dropdown-menu roo-dynamic',
18934 cls: 'datepicker-months',
18938 cls: 'table-condensed',
18940 Roo.bootstrap.DateField.content
18960 * @class Roo.bootstrap.CheckBox
18961 * @extends Roo.bootstrap.Input
18962 * Bootstrap CheckBox class
18964 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18965 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18966 * @cfg {String} boxLabel The text that appears beside the checkbox
18967 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18968 * @cfg {Boolean} checked initnal the element
18969 * @cfg {Boolean} inline inline the element (default false)
18970 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18973 * Create a new CheckBox
18974 * @param {Object} config The config object
18977 Roo.bootstrap.CheckBox = function(config){
18978 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18983 * Fires when the element is checked or unchecked.
18984 * @param {Roo.bootstrap.CheckBox} this This input
18985 * @param {Boolean} checked The new checked value
18992 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18994 inputType: 'checkbox',
19002 getAutoCreate : function()
19004 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19010 cfg.cls = 'form-group ' + this.inputType; //input-group
19013 cfg.cls += ' ' + this.inputType + '-inline';
19019 type : this.inputType,
19020 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
19021 cls : 'roo-' + this.inputType, //'form-box',
19022 placeholder : this.placeholder || ''
19026 if (this.weight) { // Validity check?
19027 cfg.cls += " " + this.inputType + "-" + this.weight;
19030 if (this.disabled) {
19031 input.disabled=true;
19035 input.checked = this.checked;
19039 input.name = this.name;
19043 input.cls += ' input-' + this.size;
19048 ['xs','sm','md','lg'].map(function(size){
19049 if (settings[size]) {
19050 cfg.cls += ' col-' + size + '-' + settings[size];
19054 var inputblock = input;
19056 if (this.before || this.after) {
19059 cls : 'input-group',
19064 inputblock.cn.push({
19066 cls : 'input-group-addon',
19071 inputblock.cn.push(input);
19074 inputblock.cn.push({
19076 cls : 'input-group-addon',
19083 if (align ==='left' && this.fieldLabel.length) {
19084 // Roo.log("left and has label");
19090 cls : 'control-label col-md-' + this.labelWidth,
19091 html : this.fieldLabel
19095 cls : "col-md-" + (12 - this.labelWidth),
19102 } else if ( this.fieldLabel.length) {
19103 // Roo.log(" label");
19107 tag: this.boxLabel ? 'span' : 'label',
19109 cls: 'control-label box-input-label',
19110 //cls : 'input-group-addon',
19111 html : this.fieldLabel
19121 // Roo.log(" no label && no align");
19122 cfg.cn = [ inputblock ] ;
19128 var boxLabelCfg = {
19130 //'for': id, // box label is handled by onclick - so no for...
19132 html: this.boxLabel
19136 boxLabelCfg.tooltip = this.tooltip;
19139 cfg.cn.push(boxLabelCfg);
19149 * return the real input element.
19151 inputEl: function ()
19153 return this.el.select('input.roo-' + this.inputType,true).first();
19156 labelEl: function()
19158 return this.el.select('label.control-label',true).first();
19160 /* depricated... */
19164 return this.labelEl();
19167 boxLabelEl: function()
19169 return this.el.select('label.box-label',true).first();
19172 initEvents : function()
19174 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19176 this.inputEl().on('click', this.onClick, this);
19178 if (this.boxLabel) {
19179 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19182 this.startValue = this.getValue();
19185 Roo.bootstrap.CheckBox.register(this);
19189 onClick : function()
19191 this.setChecked(!this.checked);
19194 setChecked : function(state,suppressEvent)
19196 this.startValue = this.getValue();
19198 if(this.inputType == 'radio'){
19200 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19201 e.dom.checked = false;
19204 this.inputEl().dom.checked = true;
19206 this.inputEl().dom.value = this.inputValue;
19208 if(suppressEvent !== true){
19209 this.fireEvent('check', this, true);
19217 this.checked = state;
19219 this.inputEl().dom.checked = state;
19221 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19223 if(suppressEvent !== true){
19224 this.fireEvent('check', this, state);
19230 getValue : function()
19232 if(this.inputType == 'radio'){
19233 return this.getGroupValue();
19236 return this.inputEl().getValue();
19240 getGroupValue : function()
19242 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19246 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19249 setValue : function(v,suppressEvent)
19251 if(this.inputType == 'radio'){
19252 this.setGroupValue(v, suppressEvent);
19256 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19261 setGroupValue : function(v, suppressEvent)
19263 this.startValue = this.getValue();
19265 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19266 e.dom.checked = false;
19268 if(e.dom.value == v){
19269 e.dom.checked = true;
19273 if(suppressEvent !== true){
19274 this.fireEvent('check', this, true);
19282 validate : function()
19286 (this.inputType == 'radio' && this.validateRadio()) ||
19287 (this.inputType == 'checkbox' && this.validateCheckbox())
19293 this.markInvalid();
19297 validateRadio : function()
19301 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19302 if(!e.dom.checked){
19314 validateCheckbox : function()
19317 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19320 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19328 for(var i in group){
19333 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19340 * Mark this field as valid
19342 markValid : function()
19344 if(this.allowBlank){
19350 this.fireEvent('valid', this);
19352 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19355 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19362 if(this.inputType == 'radio'){
19363 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19364 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19365 e.findParent('.form-group', false, true).addClass(_this.validClass);
19372 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19373 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19377 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19383 for(var i in group){
19384 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19385 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19390 * Mark this field as invalid
19391 * @param {String} msg The validation message
19393 markInvalid : function(msg)
19395 if(this.allowBlank){
19401 this.fireEvent('invalid', this, msg);
19403 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19406 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19410 label.markInvalid();
19413 if(this.inputType == 'radio'){
19414 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19415 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19416 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19423 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19424 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19428 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19434 for(var i in group){
19435 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19436 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19443 Roo.apply(Roo.bootstrap.CheckBox, {
19448 * register a CheckBox Group
19449 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19451 register : function(checkbox)
19453 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19454 this.groups[checkbox.groupId] = {};
19457 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19461 this.groups[checkbox.groupId][checkbox.name] = checkbox;
19465 * fetch a CheckBox Group based on the group ID
19466 * @param {string} the group ID
19467 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19469 get: function(groupId) {
19470 if (typeof(this.groups[groupId]) == 'undefined') {
19474 return this.groups[groupId] ;
19486 *<div class="radio">
19488 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19489 Option one is this and that—be sure to include why it's great
19496 *<label class="radio-inline">fieldLabel</label>
19497 *<label class="radio-inline">
19498 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19506 * @class Roo.bootstrap.Radio
19507 * @extends Roo.bootstrap.CheckBox
19508 * Bootstrap Radio class
19511 * Create a new Radio
19512 * @param {Object} config The config object
19515 Roo.bootstrap.Radio = function(config){
19516 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19520 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19522 inputType: 'radio',
19526 getAutoCreate : function()
19528 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19529 align = align || 'left'; // default...
19536 tag : this.inline ? 'span' : 'div',
19541 var inline = this.inline ? ' radio-inline' : '';
19545 // does not need for, as we wrap the input with it..
19547 cls : 'control-label box-label' + inline,
19550 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19554 //cls : 'control-label' + inline,
19555 html : this.fieldLabel,
19556 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19565 type : this.inputType,
19566 //value : (!this.checked) ? this.valueOff : this.inputValue,
19567 value : this.inputValue,
19569 placeholder : this.placeholder || '' // ?? needed????
19572 if (this.weight) { // Validity check?
19573 input.cls += " radio-" + this.weight;
19575 if (this.disabled) {
19576 input.disabled=true;
19580 input.checked = this.checked;
19584 input.name = this.name;
19588 input.cls += ' input-' + this.size;
19591 //?? can span's inline have a width??
19594 ['xs','sm','md','lg'].map(function(size){
19595 if (settings[size]) {
19596 cfg.cls += ' col-' + size + '-' + settings[size];
19600 var inputblock = input;
19602 if (this.before || this.after) {
19605 cls : 'input-group',
19610 inputblock.cn.push({
19612 cls : 'input-group-addon',
19616 inputblock.cn.push(input);
19618 inputblock.cn.push({
19620 cls : 'input-group-addon',
19628 if (this.fieldLabel && this.fieldLabel.length) {
19629 cfg.cn.push(fieldLabel);
19632 // normal bootstrap puts the input inside the label.
19633 // however with our styled version - it has to go after the input.
19635 //lbl.cn.push(inputblock);
19639 cls: 'radio' + inline,
19646 cfg.cn.push( lblwrap);
19651 html: this.boxLabel
19660 initEvents : function()
19662 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19664 this.inputEl().on('click', this.onClick, this);
19665 if (this.boxLabel) {
19666 //Roo.log('find label');
19667 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19672 inputEl: function ()
19674 return this.el.select('input.roo-radio',true).first();
19676 onClick : function()
19679 this.setChecked(true);
19682 setChecked : function(state,suppressEvent)
19685 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19686 v.dom.checked = false;
19689 Roo.log(this.inputEl().dom);
19690 this.checked = state;
19691 this.inputEl().dom.checked = state;
19693 if(suppressEvent !== true){
19694 this.fireEvent('check', this, state);
19697 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19701 getGroupValue : function()
19704 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19705 if(v.dom.checked == true){
19706 value = v.dom.value;
19714 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19715 * @return {Mixed} value The field value
19717 getValue : function(){
19718 return this.getGroupValue();
19724 //<script type="text/javascript">
19727 * Based Ext JS Library 1.1.1
19728 * Copyright(c) 2006-2007, Ext JS, LLC.
19734 * @class Roo.HtmlEditorCore
19735 * @extends Roo.Component
19736 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19738 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19741 Roo.HtmlEditorCore = function(config){
19744 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19749 * @event initialize
19750 * Fires when the editor is fully initialized (including the iframe)
19751 * @param {Roo.HtmlEditorCore} this
19756 * Fires when the editor is first receives the focus. Any insertion must wait
19757 * until after this event.
19758 * @param {Roo.HtmlEditorCore} this
19762 * @event beforesync
19763 * Fires before the textarea is updated with content from the editor iframe. Return false
19764 * to cancel the sync.
19765 * @param {Roo.HtmlEditorCore} this
19766 * @param {String} html
19770 * @event beforepush
19771 * Fires before the iframe editor is updated with content from the textarea. Return false
19772 * to cancel the push.
19773 * @param {Roo.HtmlEditorCore} this
19774 * @param {String} html
19779 * Fires when the textarea is updated with content from the editor iframe.
19780 * @param {Roo.HtmlEditorCore} this
19781 * @param {String} html
19786 * Fires when the iframe editor is updated with content from the textarea.
19787 * @param {Roo.HtmlEditorCore} this
19788 * @param {String} html
19793 * @event editorevent
19794 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19795 * @param {Roo.HtmlEditorCore} this
19801 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19803 // defaults : white / black...
19804 this.applyBlacklists();
19811 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19815 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19821 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19826 * @cfg {Number} height (in pixels)
19830 * @cfg {Number} width (in pixels)
19835 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19838 stylesheets: false,
19843 // private properties
19844 validationEvent : false,
19846 initialized : false,
19848 sourceEditMode : false,
19849 onFocus : Roo.emptyFn,
19851 hideMode:'offsets',
19855 // blacklist + whitelisted elements..
19862 * Protected method that will not generally be called directly. It
19863 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19864 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19866 getDocMarkup : function(){
19870 // inherit styels from page...??
19871 if (this.stylesheets === false) {
19873 Roo.get(document.head).select('style').each(function(node) {
19874 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19877 Roo.get(document.head).select('link').each(function(node) {
19878 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19881 } else if (!this.stylesheets.length) {
19883 st = '<style type="text/css">' +
19884 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19890 st += '<style type="text/css">' +
19891 'IMG { cursor: pointer } ' +
19895 return '<html><head>' + st +
19896 //<style type="text/css">' +
19897 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19899 ' </head><body class="roo-htmleditor-body"></body></html>';
19903 onRender : function(ct, position)
19906 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19907 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19910 this.el.dom.style.border = '0 none';
19911 this.el.dom.setAttribute('tabIndex', -1);
19912 this.el.addClass('x-hidden hide');
19916 if(Roo.isIE){ // fix IE 1px bogus margin
19917 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19921 this.frameId = Roo.id();
19925 var iframe = this.owner.wrap.createChild({
19927 cls: 'form-control', // bootstrap..
19929 name: this.frameId,
19930 frameBorder : 'no',
19931 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19936 this.iframe = iframe.dom;
19938 this.assignDocWin();
19940 this.doc.designMode = 'on';
19943 this.doc.write(this.getDocMarkup());
19947 var task = { // must defer to wait for browser to be ready
19949 //console.log("run task?" + this.doc.readyState);
19950 this.assignDocWin();
19951 if(this.doc.body || this.doc.readyState == 'complete'){
19953 this.doc.designMode="on";
19957 Roo.TaskMgr.stop(task);
19958 this.initEditor.defer(10, this);
19965 Roo.TaskMgr.start(task);
19970 onResize : function(w, h)
19972 Roo.log('resize: ' +w + ',' + h );
19973 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19977 if(typeof w == 'number'){
19979 this.iframe.style.width = w + 'px';
19981 if(typeof h == 'number'){
19983 this.iframe.style.height = h + 'px';
19985 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19992 * Toggles the editor between standard and source edit mode.
19993 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19995 toggleSourceEdit : function(sourceEditMode){
19997 this.sourceEditMode = sourceEditMode === true;
19999 if(this.sourceEditMode){
20001 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20004 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20005 //this.iframe.className = '';
20008 //this.setSize(this.owner.wrap.getSize());
20009 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20016 * Protected method that will not generally be called directly. If you need/want
20017 * custom HTML cleanup, this is the method you should override.
20018 * @param {String} html The HTML to be cleaned
20019 * return {String} The cleaned HTML
20021 cleanHtml : function(html){
20022 html = String(html);
20023 if(html.length > 5){
20024 if(Roo.isSafari){ // strip safari nonsense
20025 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20028 if(html == ' '){
20035 * HTML Editor -> Textarea
20036 * Protected method that will not generally be called directly. Syncs the contents
20037 * of the editor iframe with the textarea.
20039 syncValue : function(){
20040 if(this.initialized){
20041 var bd = (this.doc.body || this.doc.documentElement);
20042 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20043 var html = bd.innerHTML;
20045 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20046 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20048 html = '<div style="'+m[0]+'">' + html + '</div>';
20051 html = this.cleanHtml(html);
20052 // fix up the special chars.. normaly like back quotes in word...
20053 // however we do not want to do this with chinese..
20054 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20055 var cc = b.charCodeAt();
20057 (cc >= 0x4E00 && cc < 0xA000 ) ||
20058 (cc >= 0x3400 && cc < 0x4E00 ) ||
20059 (cc >= 0xf900 && cc < 0xfb00 )
20065 if(this.owner.fireEvent('beforesync', this, html) !== false){
20066 this.el.dom.value = html;
20067 this.owner.fireEvent('sync', this, html);
20073 * Protected method that will not generally be called directly. Pushes the value of the textarea
20074 * into the iframe editor.
20076 pushValue : function(){
20077 if(this.initialized){
20078 var v = this.el.dom.value.trim();
20080 // if(v.length < 1){
20084 if(this.owner.fireEvent('beforepush', this, v) !== false){
20085 var d = (this.doc.body || this.doc.documentElement);
20087 this.cleanUpPaste();
20088 this.el.dom.value = d.innerHTML;
20089 this.owner.fireEvent('push', this, v);
20095 deferFocus : function(){
20096 this.focus.defer(10, this);
20100 focus : function(){
20101 if(this.win && !this.sourceEditMode){
20108 assignDocWin: function()
20110 var iframe = this.iframe;
20113 this.doc = iframe.contentWindow.document;
20114 this.win = iframe.contentWindow;
20116 // if (!Roo.get(this.frameId)) {
20119 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20120 // this.win = Roo.get(this.frameId).dom.contentWindow;
20122 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20126 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20127 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20132 initEditor : function(){
20133 //console.log("INIT EDITOR");
20134 this.assignDocWin();
20138 this.doc.designMode="on";
20140 this.doc.write(this.getDocMarkup());
20143 var dbody = (this.doc.body || this.doc.documentElement);
20144 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20145 // this copies styles from the containing element into thsi one..
20146 // not sure why we need all of this..
20147 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20149 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20150 //ss['background-attachment'] = 'fixed'; // w3c
20151 dbody.bgProperties = 'fixed'; // ie
20152 //Roo.DomHelper.applyStyles(dbody, ss);
20153 Roo.EventManager.on(this.doc, {
20154 //'mousedown': this.onEditorEvent,
20155 'mouseup': this.onEditorEvent,
20156 'dblclick': this.onEditorEvent,
20157 'click': this.onEditorEvent,
20158 'keyup': this.onEditorEvent,
20163 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20165 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20166 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20168 this.initialized = true;
20170 this.owner.fireEvent('initialize', this);
20175 onDestroy : function(){
20181 //for (var i =0; i < this.toolbars.length;i++) {
20182 // // fixme - ask toolbars for heights?
20183 // this.toolbars[i].onDestroy();
20186 //this.wrap.dom.innerHTML = '';
20187 //this.wrap.remove();
20192 onFirstFocus : function(){
20194 this.assignDocWin();
20197 this.activated = true;
20200 if(Roo.isGecko){ // prevent silly gecko errors
20202 var s = this.win.getSelection();
20203 if(!s.focusNode || s.focusNode.nodeType != 3){
20204 var r = s.getRangeAt(0);
20205 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20210 this.execCmd('useCSS', true);
20211 this.execCmd('styleWithCSS', false);
20214 this.owner.fireEvent('activate', this);
20218 adjustFont: function(btn){
20219 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20220 //if(Roo.isSafari){ // safari
20223 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20224 if(Roo.isSafari){ // safari
20225 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20226 v = (v < 10) ? 10 : v;
20227 v = (v > 48) ? 48 : v;
20228 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20233 v = Math.max(1, v+adjust);
20235 this.execCmd('FontSize', v );
20238 onEditorEvent : function(e)
20240 this.owner.fireEvent('editorevent', this, e);
20241 // this.updateToolbar();
20242 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20245 insertTag : function(tg)
20247 // could be a bit smarter... -> wrap the current selected tRoo..
20248 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20250 range = this.createRange(this.getSelection());
20251 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20252 wrappingNode.appendChild(range.extractContents());
20253 range.insertNode(wrappingNode);
20260 this.execCmd("formatblock", tg);
20264 insertText : function(txt)
20268 var range = this.createRange();
20269 range.deleteContents();
20270 //alert(Sender.getAttribute('label'));
20272 range.insertNode(this.doc.createTextNode(txt));
20278 * Executes a Midas editor command on the editor document and performs necessary focus and
20279 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20280 * @param {String} cmd The Midas command
20281 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20283 relayCmd : function(cmd, value){
20285 this.execCmd(cmd, value);
20286 this.owner.fireEvent('editorevent', this);
20287 //this.updateToolbar();
20288 this.owner.deferFocus();
20292 * Executes a Midas editor command directly on the editor document.
20293 * For visual commands, you should use {@link #relayCmd} instead.
20294 * <b>This should only be called after the editor is initialized.</b>
20295 * @param {String} cmd The Midas command
20296 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20298 execCmd : function(cmd, value){
20299 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20306 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20308 * @param {String} text | dom node..
20310 insertAtCursor : function(text)
20315 if(!this.activated){
20321 var r = this.doc.selection.createRange();
20332 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20336 // from jquery ui (MIT licenced)
20338 var win = this.win;
20340 if (win.getSelection && win.getSelection().getRangeAt) {
20341 range = win.getSelection().getRangeAt(0);
20342 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20343 range.insertNode(node);
20344 } else if (win.document.selection && win.document.selection.createRange) {
20345 // no firefox support
20346 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20347 win.document.selection.createRange().pasteHTML(txt);
20349 // no firefox support
20350 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20351 this.execCmd('InsertHTML', txt);
20360 mozKeyPress : function(e){
20362 var c = e.getCharCode(), cmd;
20365 c = String.fromCharCode(c).toLowerCase();
20379 this.cleanUpPaste.defer(100, this);
20387 e.preventDefault();
20395 fixKeys : function(){ // load time branching for fastest keydown performance
20397 return function(e){
20398 var k = e.getKey(), r;
20401 r = this.doc.selection.createRange();
20404 r.pasteHTML('    ');
20411 r = this.doc.selection.createRange();
20413 var target = r.parentElement();
20414 if(!target || target.tagName.toLowerCase() != 'li'){
20416 r.pasteHTML('<br />');
20422 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20423 this.cleanUpPaste.defer(100, this);
20429 }else if(Roo.isOpera){
20430 return function(e){
20431 var k = e.getKey();
20435 this.execCmd('InsertHTML','    ');
20438 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20439 this.cleanUpPaste.defer(100, this);
20444 }else if(Roo.isSafari){
20445 return function(e){
20446 var k = e.getKey();
20450 this.execCmd('InsertText','\t');
20454 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20455 this.cleanUpPaste.defer(100, this);
20463 getAllAncestors: function()
20465 var p = this.getSelectedNode();
20468 a.push(p); // push blank onto stack..
20469 p = this.getParentElement();
20473 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20477 a.push(this.doc.body);
20481 lastSelNode : false,
20484 getSelection : function()
20486 this.assignDocWin();
20487 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20490 getSelectedNode: function()
20492 // this may only work on Gecko!!!
20494 // should we cache this!!!!
20499 var range = this.createRange(this.getSelection()).cloneRange();
20502 var parent = range.parentElement();
20504 var testRange = range.duplicate();
20505 testRange.moveToElementText(parent);
20506 if (testRange.inRange(range)) {
20509 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20512 parent = parent.parentElement;
20517 // is ancestor a text element.
20518 var ac = range.commonAncestorContainer;
20519 if (ac.nodeType == 3) {
20520 ac = ac.parentNode;
20523 var ar = ac.childNodes;
20526 var other_nodes = [];
20527 var has_other_nodes = false;
20528 for (var i=0;i<ar.length;i++) {
20529 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20532 // fullly contained node.
20534 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20539 // probably selected..
20540 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20541 other_nodes.push(ar[i]);
20545 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20550 has_other_nodes = true;
20552 if (!nodes.length && other_nodes.length) {
20553 nodes= other_nodes;
20555 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20561 createRange: function(sel)
20563 // this has strange effects when using with
20564 // top toolbar - not sure if it's a great idea.
20565 //this.editor.contentWindow.focus();
20566 if (typeof sel != "undefined") {
20568 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20570 return this.doc.createRange();
20573 return this.doc.createRange();
20576 getParentElement: function()
20579 this.assignDocWin();
20580 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20582 var range = this.createRange(sel);
20585 var p = range.commonAncestorContainer;
20586 while (p.nodeType == 3) { // text node
20597 * Range intersection.. the hard stuff...
20601 * [ -- selected range --- ]
20605 * if end is before start or hits it. fail.
20606 * if start is after end or hits it fail.
20608 * if either hits (but other is outside. - then it's not
20614 // @see http://www.thismuchiknow.co.uk/?p=64.
20615 rangeIntersectsNode : function(range, node)
20617 var nodeRange = node.ownerDocument.createRange();
20619 nodeRange.selectNode(node);
20621 nodeRange.selectNodeContents(node);
20624 var rangeStartRange = range.cloneRange();
20625 rangeStartRange.collapse(true);
20627 var rangeEndRange = range.cloneRange();
20628 rangeEndRange.collapse(false);
20630 var nodeStartRange = nodeRange.cloneRange();
20631 nodeStartRange.collapse(true);
20633 var nodeEndRange = nodeRange.cloneRange();
20634 nodeEndRange.collapse(false);
20636 return rangeStartRange.compareBoundaryPoints(
20637 Range.START_TO_START, nodeEndRange) == -1 &&
20638 rangeEndRange.compareBoundaryPoints(
20639 Range.START_TO_START, nodeStartRange) == 1;
20643 rangeCompareNode : function(range, node)
20645 var nodeRange = node.ownerDocument.createRange();
20647 nodeRange.selectNode(node);
20649 nodeRange.selectNodeContents(node);
20653 range.collapse(true);
20655 nodeRange.collapse(true);
20657 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20658 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20660 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20662 var nodeIsBefore = ss == 1;
20663 var nodeIsAfter = ee == -1;
20665 if (nodeIsBefore && nodeIsAfter) {
20668 if (!nodeIsBefore && nodeIsAfter) {
20669 return 1; //right trailed.
20672 if (nodeIsBefore && !nodeIsAfter) {
20673 return 2; // left trailed.
20679 // private? - in a new class?
20680 cleanUpPaste : function()
20682 // cleans up the whole document..
20683 Roo.log('cleanuppaste');
20685 this.cleanUpChildren(this.doc.body);
20686 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20687 if (clean != this.doc.body.innerHTML) {
20688 this.doc.body.innerHTML = clean;
20693 cleanWordChars : function(input) {// change the chars to hex code
20694 var he = Roo.HtmlEditorCore;
20696 var output = input;
20697 Roo.each(he.swapCodes, function(sw) {
20698 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20700 output = output.replace(swapper, sw[1]);
20707 cleanUpChildren : function (n)
20709 if (!n.childNodes.length) {
20712 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20713 this.cleanUpChild(n.childNodes[i]);
20720 cleanUpChild : function (node)
20723 //console.log(node);
20724 if (node.nodeName == "#text") {
20725 // clean up silly Windows -- stuff?
20728 if (node.nodeName == "#comment") {
20729 node.parentNode.removeChild(node);
20730 // clean up silly Windows -- stuff?
20733 var lcname = node.tagName.toLowerCase();
20734 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20735 // whitelist of tags..
20737 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20739 node.parentNode.removeChild(node);
20744 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20746 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20747 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20749 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20750 // remove_keep_children = true;
20753 if (remove_keep_children) {
20754 this.cleanUpChildren(node);
20755 // inserts everything just before this node...
20756 while (node.childNodes.length) {
20757 var cn = node.childNodes[0];
20758 node.removeChild(cn);
20759 node.parentNode.insertBefore(cn, node);
20761 node.parentNode.removeChild(node);
20765 if (!node.attributes || !node.attributes.length) {
20766 this.cleanUpChildren(node);
20770 function cleanAttr(n,v)
20773 if (v.match(/^\./) || v.match(/^\//)) {
20776 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20779 if (v.match(/^#/)) {
20782 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20783 node.removeAttribute(n);
20787 var cwhite = this.cwhite;
20788 var cblack = this.cblack;
20790 function cleanStyle(n,v)
20792 if (v.match(/expression/)) { //XSS?? should we even bother..
20793 node.removeAttribute(n);
20797 var parts = v.split(/;/);
20800 Roo.each(parts, function(p) {
20801 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20805 var l = p.split(':').shift().replace(/\s+/g,'');
20806 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20808 if ( cwhite.length && cblack.indexOf(l) > -1) {
20809 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20810 //node.removeAttribute(n);
20814 // only allow 'c whitelisted system attributes'
20815 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20816 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20817 //node.removeAttribute(n);
20827 if (clean.length) {
20828 node.setAttribute(n, clean.join(';'));
20830 node.removeAttribute(n);
20836 for (var i = node.attributes.length-1; i > -1 ; i--) {
20837 var a = node.attributes[i];
20840 if (a.name.toLowerCase().substr(0,2)=='on') {
20841 node.removeAttribute(a.name);
20844 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20845 node.removeAttribute(a.name);
20848 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20849 cleanAttr(a.name,a.value); // fixme..
20852 if (a.name == 'style') {
20853 cleanStyle(a.name,a.value);
20856 /// clean up MS crap..
20857 // tecnically this should be a list of valid class'es..
20860 if (a.name == 'class') {
20861 if (a.value.match(/^Mso/)) {
20862 node.className = '';
20865 if (a.value.match(/body/)) {
20866 node.className = '';
20877 this.cleanUpChildren(node);
20883 * Clean up MS wordisms...
20885 cleanWord : function(node)
20890 this.cleanWord(this.doc.body);
20893 if (node.nodeName == "#text") {
20894 // clean up silly Windows -- stuff?
20897 if (node.nodeName == "#comment") {
20898 node.parentNode.removeChild(node);
20899 // clean up silly Windows -- stuff?
20903 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20904 node.parentNode.removeChild(node);
20908 // remove - but keep children..
20909 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20910 while (node.childNodes.length) {
20911 var cn = node.childNodes[0];
20912 node.removeChild(cn);
20913 node.parentNode.insertBefore(cn, node);
20915 node.parentNode.removeChild(node);
20916 this.iterateChildren(node, this.cleanWord);
20920 if (node.className.length) {
20922 var cn = node.className.split(/\W+/);
20924 Roo.each(cn, function(cls) {
20925 if (cls.match(/Mso[a-zA-Z]+/)) {
20930 node.className = cna.length ? cna.join(' ') : '';
20932 node.removeAttribute("class");
20936 if (node.hasAttribute("lang")) {
20937 node.removeAttribute("lang");
20940 if (node.hasAttribute("style")) {
20942 var styles = node.getAttribute("style").split(";");
20944 Roo.each(styles, function(s) {
20945 if (!s.match(/:/)) {
20948 var kv = s.split(":");
20949 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20952 // what ever is left... we allow.
20955 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20956 if (!nstyle.length) {
20957 node.removeAttribute('style');
20960 this.iterateChildren(node, this.cleanWord);
20966 * iterateChildren of a Node, calling fn each time, using this as the scole..
20967 * @param {DomNode} node node to iterate children of.
20968 * @param {Function} fn method of this class to call on each item.
20970 iterateChildren : function(node, fn)
20972 if (!node.childNodes.length) {
20975 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20976 fn.call(this, node.childNodes[i])
20982 * cleanTableWidths.
20984 * Quite often pasting from word etc.. results in tables with column and widths.
20985 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20988 cleanTableWidths : function(node)
20993 this.cleanTableWidths(this.doc.body);
20998 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21001 Roo.log(node.tagName);
21002 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21003 this.iterateChildren(node, this.cleanTableWidths);
21006 if (node.hasAttribute('width')) {
21007 node.removeAttribute('width');
21011 if (node.hasAttribute("style")) {
21014 var styles = node.getAttribute("style").split(";");
21016 Roo.each(styles, function(s) {
21017 if (!s.match(/:/)) {
21020 var kv = s.split(":");
21021 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21024 // what ever is left... we allow.
21027 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21028 if (!nstyle.length) {
21029 node.removeAttribute('style');
21033 this.iterateChildren(node, this.cleanTableWidths);
21041 domToHTML : function(currentElement, depth, nopadtext) {
21043 depth = depth || 0;
21044 nopadtext = nopadtext || false;
21046 if (!currentElement) {
21047 return this.domToHTML(this.doc.body);
21050 //Roo.log(currentElement);
21052 var allText = false;
21053 var nodeName = currentElement.nodeName;
21054 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21056 if (nodeName == '#text') {
21058 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21063 if (nodeName != 'BODY') {
21066 // Prints the node tagName, such as <A>, <IMG>, etc
21069 for(i = 0; i < currentElement.attributes.length;i++) {
21071 var aname = currentElement.attributes.item(i).name;
21072 if (!currentElement.attributes.item(i).value.length) {
21075 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21078 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21087 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21090 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21095 // Traverse the tree
21097 var currentElementChild = currentElement.childNodes.item(i);
21098 var allText = true;
21099 var innerHTML = '';
21101 while (currentElementChild) {
21102 // Formatting code (indent the tree so it looks nice on the screen)
21103 var nopad = nopadtext;
21104 if (lastnode == 'SPAN') {
21108 if (currentElementChild.nodeName == '#text') {
21109 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21110 toadd = nopadtext ? toadd : toadd.trim();
21111 if (!nopad && toadd.length > 80) {
21112 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21114 innerHTML += toadd;
21117 currentElementChild = currentElement.childNodes.item(i);
21123 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21125 // Recursively traverse the tree structure of the child node
21126 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21127 lastnode = currentElementChild.nodeName;
21129 currentElementChild=currentElement.childNodes.item(i);
21135 // The remaining code is mostly for formatting the tree
21136 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21141 ret+= "</"+tagName+">";
21147 applyBlacklists : function()
21149 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21150 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21154 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21155 if (b.indexOf(tag) > -1) {
21158 this.white.push(tag);
21162 Roo.each(w, function(tag) {
21163 if (b.indexOf(tag) > -1) {
21166 if (this.white.indexOf(tag) > -1) {
21169 this.white.push(tag);
21174 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21175 if (w.indexOf(tag) > -1) {
21178 this.black.push(tag);
21182 Roo.each(b, function(tag) {
21183 if (w.indexOf(tag) > -1) {
21186 if (this.black.indexOf(tag) > -1) {
21189 this.black.push(tag);
21194 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21195 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21199 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21200 if (b.indexOf(tag) > -1) {
21203 this.cwhite.push(tag);
21207 Roo.each(w, function(tag) {
21208 if (b.indexOf(tag) > -1) {
21211 if (this.cwhite.indexOf(tag) > -1) {
21214 this.cwhite.push(tag);
21219 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21220 if (w.indexOf(tag) > -1) {
21223 this.cblack.push(tag);
21227 Roo.each(b, function(tag) {
21228 if (w.indexOf(tag) > -1) {
21231 if (this.cblack.indexOf(tag) > -1) {
21234 this.cblack.push(tag);
21239 setStylesheets : function(stylesheets)
21241 if(typeof(stylesheets) == 'string'){
21242 Roo.get(this.iframe.contentDocument.head).createChild({
21244 rel : 'stylesheet',
21253 Roo.each(stylesheets, function(s) {
21258 Roo.get(_this.iframe.contentDocument.head).createChild({
21260 rel : 'stylesheet',
21269 removeStylesheets : function()
21273 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21278 // hide stuff that is not compatible
21292 * @event specialkey
21296 * @cfg {String} fieldClass @hide
21299 * @cfg {String} focusClass @hide
21302 * @cfg {String} autoCreate @hide
21305 * @cfg {String} inputType @hide
21308 * @cfg {String} invalidClass @hide
21311 * @cfg {String} invalidText @hide
21314 * @cfg {String} msgFx @hide
21317 * @cfg {String} validateOnBlur @hide
21321 Roo.HtmlEditorCore.white = [
21322 'area', 'br', 'img', 'input', 'hr', 'wbr',
21324 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21325 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21326 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21327 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21328 'table', 'ul', 'xmp',
21330 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21333 'dir', 'menu', 'ol', 'ul', 'dl',
21339 Roo.HtmlEditorCore.black = [
21340 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21342 'base', 'basefont', 'bgsound', 'blink', 'body',
21343 'frame', 'frameset', 'head', 'html', 'ilayer',
21344 'iframe', 'layer', 'link', 'meta', 'object',
21345 'script', 'style' ,'title', 'xml' // clean later..
21347 Roo.HtmlEditorCore.clean = [
21348 'script', 'style', 'title', 'xml'
21350 Roo.HtmlEditorCore.remove = [
21355 Roo.HtmlEditorCore.ablack = [
21359 Roo.HtmlEditorCore.aclean = [
21360 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21364 Roo.HtmlEditorCore.pwhite= [
21365 'http', 'https', 'mailto'
21368 // white listed style attributes.
21369 Roo.HtmlEditorCore.cwhite= [
21370 // 'text-align', /// default is to allow most things..
21376 // black listed style attributes.
21377 Roo.HtmlEditorCore.cblack= [
21378 // 'font-size' -- this can be set by the project
21382 Roo.HtmlEditorCore.swapCodes =[
21401 * @class Roo.bootstrap.HtmlEditor
21402 * @extends Roo.bootstrap.TextArea
21403 * Bootstrap HtmlEditor class
21406 * Create a new HtmlEditor
21407 * @param {Object} config The config object
21410 Roo.bootstrap.HtmlEditor = function(config){
21411 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21412 if (!this.toolbars) {
21413 this.toolbars = [];
21415 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21418 * @event initialize
21419 * Fires when the editor is fully initialized (including the iframe)
21420 * @param {HtmlEditor} this
21425 * Fires when the editor is first receives the focus. Any insertion must wait
21426 * until after this event.
21427 * @param {HtmlEditor} this
21431 * @event beforesync
21432 * Fires before the textarea is updated with content from the editor iframe. Return false
21433 * to cancel the sync.
21434 * @param {HtmlEditor} this
21435 * @param {String} html
21439 * @event beforepush
21440 * Fires before the iframe editor is updated with content from the textarea. Return false
21441 * to cancel the push.
21442 * @param {HtmlEditor} this
21443 * @param {String} html
21448 * Fires when the textarea is updated with content from the editor iframe.
21449 * @param {HtmlEditor} this
21450 * @param {String} html
21455 * Fires when the iframe editor is updated with content from the textarea.
21456 * @param {HtmlEditor} this
21457 * @param {String} html
21461 * @event editmodechange
21462 * Fires when the editor switches edit modes
21463 * @param {HtmlEditor} this
21464 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21466 editmodechange: true,
21468 * @event editorevent
21469 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21470 * @param {HtmlEditor} this
21474 * @event firstfocus
21475 * Fires when on first focus - needed by toolbars..
21476 * @param {HtmlEditor} this
21481 * Auto save the htmlEditor value as a file into Events
21482 * @param {HtmlEditor} this
21486 * @event savedpreview
21487 * preview the saved version of htmlEditor
21488 * @param {HtmlEditor} this
21495 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21499 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21504 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21509 * @cfg {Number} height (in pixels)
21513 * @cfg {Number} width (in pixels)
21518 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21521 stylesheets: false,
21526 // private properties
21527 validationEvent : false,
21529 initialized : false,
21532 onFocus : Roo.emptyFn,
21534 hideMode:'offsets',
21537 tbContainer : false,
21539 toolbarContainer :function() {
21540 return this.wrap.select('.x-html-editor-tb',true).first();
21544 * Protected method that will not generally be called directly. It
21545 * is called when the editor creates its toolbar. Override this method if you need to
21546 * add custom toolbar buttons.
21547 * @param {HtmlEditor} editor
21549 createToolbar : function(){
21551 Roo.log("create toolbars");
21553 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21554 this.toolbars[0].render(this.toolbarContainer());
21558 // if (!editor.toolbars || !editor.toolbars.length) {
21559 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21562 // for (var i =0 ; i < editor.toolbars.length;i++) {
21563 // editor.toolbars[i] = Roo.factory(
21564 // typeof(editor.toolbars[i]) == 'string' ?
21565 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21566 // Roo.bootstrap.HtmlEditor);
21567 // editor.toolbars[i].init(editor);
21573 onRender : function(ct, position)
21575 // Roo.log("Call onRender: " + this.xtype);
21577 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21579 this.wrap = this.inputEl().wrap({
21580 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21583 this.editorcore.onRender(ct, position);
21585 if (this.resizable) {
21586 this.resizeEl = new Roo.Resizable(this.wrap, {
21590 minHeight : this.height,
21591 height: this.height,
21592 handles : this.resizable,
21595 resize : function(r, w, h) {
21596 _t.onResize(w,h); // -something
21602 this.createToolbar(this);
21605 if(!this.width && this.resizable){
21606 this.setSize(this.wrap.getSize());
21608 if (this.resizeEl) {
21609 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21610 // should trigger onReize..
21616 onResize : function(w, h)
21618 Roo.log('resize: ' +w + ',' + h );
21619 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21623 if(this.inputEl() ){
21624 if(typeof w == 'number'){
21625 var aw = w - this.wrap.getFrameWidth('lr');
21626 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21629 if(typeof h == 'number'){
21630 var tbh = -11; // fixme it needs to tool bar size!
21631 for (var i =0; i < this.toolbars.length;i++) {
21632 // fixme - ask toolbars for heights?
21633 tbh += this.toolbars[i].el.getHeight();
21634 //if (this.toolbars[i].footer) {
21635 // tbh += this.toolbars[i].footer.el.getHeight();
21643 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21644 ah -= 5; // knock a few pixes off for look..
21645 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21649 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21650 this.editorcore.onResize(ew,eh);
21655 * Toggles the editor between standard and source edit mode.
21656 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21658 toggleSourceEdit : function(sourceEditMode)
21660 this.editorcore.toggleSourceEdit(sourceEditMode);
21662 if(this.editorcore.sourceEditMode){
21663 Roo.log('editor - showing textarea');
21666 // Roo.log(this.syncValue());
21668 this.inputEl().removeClass(['hide', 'x-hidden']);
21669 this.inputEl().dom.removeAttribute('tabIndex');
21670 this.inputEl().focus();
21672 Roo.log('editor - hiding textarea');
21674 // Roo.log(this.pushValue());
21677 this.inputEl().addClass(['hide', 'x-hidden']);
21678 this.inputEl().dom.setAttribute('tabIndex', -1);
21679 //this.deferFocus();
21682 if(this.resizable){
21683 this.setSize(this.wrap.getSize());
21686 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21689 // private (for BoxComponent)
21690 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21692 // private (for BoxComponent)
21693 getResizeEl : function(){
21697 // private (for BoxComponent)
21698 getPositionEl : function(){
21703 initEvents : function(){
21704 this.originalValue = this.getValue();
21708 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21711 // markInvalid : Roo.emptyFn,
21713 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21716 // clearInvalid : Roo.emptyFn,
21718 setValue : function(v){
21719 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21720 this.editorcore.pushValue();
21725 deferFocus : function(){
21726 this.focus.defer(10, this);
21730 focus : function(){
21731 this.editorcore.focus();
21737 onDestroy : function(){
21743 for (var i =0; i < this.toolbars.length;i++) {
21744 // fixme - ask toolbars for heights?
21745 this.toolbars[i].onDestroy();
21748 this.wrap.dom.innerHTML = '';
21749 this.wrap.remove();
21754 onFirstFocus : function(){
21755 //Roo.log("onFirstFocus");
21756 this.editorcore.onFirstFocus();
21757 for (var i =0; i < this.toolbars.length;i++) {
21758 this.toolbars[i].onFirstFocus();
21764 syncValue : function()
21766 this.editorcore.syncValue();
21769 pushValue : function()
21771 this.editorcore.pushValue();
21775 // hide stuff that is not compatible
21789 * @event specialkey
21793 * @cfg {String} fieldClass @hide
21796 * @cfg {String} focusClass @hide
21799 * @cfg {String} autoCreate @hide
21802 * @cfg {String} inputType @hide
21805 * @cfg {String} invalidClass @hide
21808 * @cfg {String} invalidText @hide
21811 * @cfg {String} msgFx @hide
21814 * @cfg {String} validateOnBlur @hide
21823 Roo.namespace('Roo.bootstrap.htmleditor');
21825 * @class Roo.bootstrap.HtmlEditorToolbar1
21830 new Roo.bootstrap.HtmlEditor({
21833 new Roo.bootstrap.HtmlEditorToolbar1({
21834 disable : { fonts: 1 , format: 1, ..., ... , ...],
21840 * @cfg {Object} disable List of elements to disable..
21841 * @cfg {Array} btns List of additional buttons.
21845 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21848 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21851 Roo.apply(this, config);
21853 // default disabled, based on 'good practice'..
21854 this.disable = this.disable || {};
21855 Roo.applyIf(this.disable, {
21858 specialElements : true
21860 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21862 this.editor = config.editor;
21863 this.editorcore = config.editor.editorcore;
21865 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21867 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21868 // dont call parent... till later.
21870 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21875 editorcore : false,
21880 "h1","h2","h3","h4","h5","h6",
21882 "abbr", "acronym", "address", "cite", "samp", "var",
21886 onRender : function(ct, position)
21888 // Roo.log("Call onRender: " + this.xtype);
21890 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21892 this.el.dom.style.marginBottom = '0';
21894 var editorcore = this.editorcore;
21895 var editor= this.editor;
21898 var btn = function(id,cmd , toggle, handler){
21900 var event = toggle ? 'toggle' : 'click';
21905 xns: Roo.bootstrap,
21908 enableToggle:toggle !== false,
21910 pressed : toggle ? false : null,
21913 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21914 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21923 xns: Roo.bootstrap,
21924 glyphicon : 'font',
21928 xns: Roo.bootstrap,
21932 Roo.each(this.formats, function(f) {
21933 style.menu.items.push({
21935 xns: Roo.bootstrap,
21936 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21941 editorcore.insertTag(this.tagname);
21948 children.push(style);
21951 btn('bold',false,true);
21952 btn('italic',false,true);
21953 btn('align-left', 'justifyleft',true);
21954 btn('align-center', 'justifycenter',true);
21955 btn('align-right' , 'justifyright',true);
21956 btn('link', false, false, function(btn) {
21957 //Roo.log("create link?");
21958 var url = prompt(this.createLinkText, this.defaultLinkValue);
21959 if(url && url != 'http:/'+'/'){
21960 this.editorcore.relayCmd('createlink', url);
21963 btn('list','insertunorderedlist',true);
21964 btn('pencil', false,true, function(btn){
21967 this.toggleSourceEdit(btn.pressed);
21973 xns: Roo.bootstrap,
21978 xns: Roo.bootstrap,
21983 cog.menu.items.push({
21985 xns: Roo.bootstrap,
21986 html : Clean styles,
21991 editorcore.insertTag(this.tagname);
22000 this.xtype = 'NavSimplebar';
22002 for(var i=0;i< children.length;i++) {
22004 this.buttons.add(this.addxtypeChild(children[i]));
22008 editor.on('editorevent', this.updateToolbar, this);
22010 onBtnClick : function(id)
22012 this.editorcore.relayCmd(id);
22013 this.editorcore.focus();
22017 * Protected method that will not generally be called directly. It triggers
22018 * a toolbar update by reading the markup state of the current selection in the editor.
22020 updateToolbar: function(){
22022 if(!this.editorcore.activated){
22023 this.editor.onFirstFocus(); // is this neeed?
22027 var btns = this.buttons;
22028 var doc = this.editorcore.doc;
22029 btns.get('bold').setActive(doc.queryCommandState('bold'));
22030 btns.get('italic').setActive(doc.queryCommandState('italic'));
22031 //btns.get('underline').setActive(doc.queryCommandState('underline'));
22033 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22034 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22035 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22037 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22038 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22041 var ans = this.editorcore.getAllAncestors();
22042 if (this.formatCombo) {
22045 var store = this.formatCombo.store;
22046 this.formatCombo.setValue("");
22047 for (var i =0; i < ans.length;i++) {
22048 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22050 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22058 // hides menus... - so this cant be on a menu...
22059 Roo.bootstrap.MenuMgr.hideAll();
22061 Roo.bootstrap.MenuMgr.hideAll();
22062 //this.editorsyncValue();
22064 onFirstFocus: function() {
22065 this.buttons.each(function(item){
22069 toggleSourceEdit : function(sourceEditMode){
22072 if(sourceEditMode){
22073 Roo.log("disabling buttons");
22074 this.buttons.each( function(item){
22075 if(item.cmd != 'pencil'){
22081 Roo.log("enabling buttons");
22082 if(this.editorcore.initialized){
22083 this.buttons.each( function(item){
22089 Roo.log("calling toggole on editor");
22090 // tell the editor that it's been pressed..
22091 this.editor.toggleSourceEdit(sourceEditMode);
22101 * @class Roo.bootstrap.Table.AbstractSelectionModel
22102 * @extends Roo.util.Observable
22103 * Abstract base class for grid SelectionModels. It provides the interface that should be
22104 * implemented by descendant classes. This class should not be directly instantiated.
22107 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22108 this.locked = false;
22109 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22113 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22114 /** @ignore Called by the grid automatically. Do not call directly. */
22115 init : function(grid){
22121 * Locks the selections.
22124 this.locked = true;
22128 * Unlocks the selections.
22130 unlock : function(){
22131 this.locked = false;
22135 * Returns true if the selections are locked.
22136 * @return {Boolean}
22138 isLocked : function(){
22139 return this.locked;
22143 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22144 * @class Roo.bootstrap.Table.RowSelectionModel
22145 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22146 * It supports multiple selections and keyboard selection/navigation.
22148 * @param {Object} config
22151 Roo.bootstrap.Table.RowSelectionModel = function(config){
22152 Roo.apply(this, config);
22153 this.selections = new Roo.util.MixedCollection(false, function(o){
22158 this.lastActive = false;
22162 * @event selectionchange
22163 * Fires when the selection changes
22164 * @param {SelectionModel} this
22166 "selectionchange" : true,
22168 * @event afterselectionchange
22169 * Fires after the selection changes (eg. by key press or clicking)
22170 * @param {SelectionModel} this
22172 "afterselectionchange" : true,
22174 * @event beforerowselect
22175 * Fires when a row is selected being selected, return false to cancel.
22176 * @param {SelectionModel} this
22177 * @param {Number} rowIndex The selected index
22178 * @param {Boolean} keepExisting False if other selections will be cleared
22180 "beforerowselect" : true,
22183 * Fires when a row is selected.
22184 * @param {SelectionModel} this
22185 * @param {Number} rowIndex The selected index
22186 * @param {Roo.data.Record} r The record
22188 "rowselect" : true,
22190 * @event rowdeselect
22191 * Fires when a row is deselected.
22192 * @param {SelectionModel} this
22193 * @param {Number} rowIndex The selected index
22195 "rowdeselect" : true
22197 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22198 this.locked = false;
22201 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22203 * @cfg {Boolean} singleSelect
22204 * True to allow selection of only one row at a time (defaults to false)
22206 singleSelect : false,
22209 initEvents : function(){
22211 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22212 this.grid.on("mousedown", this.handleMouseDown, this);
22213 }else{ // allow click to work like normal
22214 this.grid.on("rowclick", this.handleDragableRowClick, this);
22217 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22218 "up" : function(e){
22220 this.selectPrevious(e.shiftKey);
22221 }else if(this.last !== false && this.lastActive !== false){
22222 var last = this.last;
22223 this.selectRange(this.last, this.lastActive-1);
22224 this.grid.getView().focusRow(this.lastActive);
22225 if(last !== false){
22229 this.selectFirstRow();
22231 this.fireEvent("afterselectionchange", this);
22233 "down" : function(e){
22235 this.selectNext(e.shiftKey);
22236 }else if(this.last !== false && this.lastActive !== false){
22237 var last = this.last;
22238 this.selectRange(this.last, this.lastActive+1);
22239 this.grid.getView().focusRow(this.lastActive);
22240 if(last !== false){
22244 this.selectFirstRow();
22246 this.fireEvent("afterselectionchange", this);
22251 var view = this.grid.view;
22252 view.on("refresh", this.onRefresh, this);
22253 view.on("rowupdated", this.onRowUpdated, this);
22254 view.on("rowremoved", this.onRemove, this);
22258 onRefresh : function(){
22259 var ds = this.grid.dataSource, i, v = this.grid.view;
22260 var s = this.selections;
22261 s.each(function(r){
22262 if((i = ds.indexOfId(r.id)) != -1){
22271 onRemove : function(v, index, r){
22272 this.selections.remove(r);
22276 onRowUpdated : function(v, index, r){
22277 if(this.isSelected(r)){
22278 v.onRowSelect(index);
22284 * @param {Array} records The records to select
22285 * @param {Boolean} keepExisting (optional) True to keep existing selections
22287 selectRecords : function(records, keepExisting){
22289 this.clearSelections();
22291 var ds = this.grid.dataSource;
22292 for(var i = 0, len = records.length; i < len; i++){
22293 this.selectRow(ds.indexOf(records[i]), true);
22298 * Gets the number of selected rows.
22301 getCount : function(){
22302 return this.selections.length;
22306 * Selects the first row in the grid.
22308 selectFirstRow : function(){
22313 * Select the last row.
22314 * @param {Boolean} keepExisting (optional) True to keep existing selections
22316 selectLastRow : function(keepExisting){
22317 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22321 * Selects the row immediately following the last selected row.
22322 * @param {Boolean} keepExisting (optional) True to keep existing selections
22324 selectNext : function(keepExisting){
22325 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22326 this.selectRow(this.last+1, keepExisting);
22327 this.grid.getView().focusRow(this.last);
22332 * Selects the row that precedes the last selected row.
22333 * @param {Boolean} keepExisting (optional) True to keep existing selections
22335 selectPrevious : function(keepExisting){
22337 this.selectRow(this.last-1, keepExisting);
22338 this.grid.getView().focusRow(this.last);
22343 * Returns the selected records
22344 * @return {Array} Array of selected records
22346 getSelections : function(){
22347 return [].concat(this.selections.items);
22351 * Returns the first selected record.
22354 getSelected : function(){
22355 return this.selections.itemAt(0);
22360 * Clears all selections.
22362 clearSelections : function(fast){
22367 var ds = this.grid.dataSource;
22368 var s = this.selections;
22369 s.each(function(r){
22370 this.deselectRow(ds.indexOfId(r.id));
22374 this.selections.clear();
22381 * Selects all rows.
22383 selectAll : function(){
22387 this.selections.clear();
22388 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22389 this.selectRow(i, true);
22394 * Returns True if there is a selection.
22395 * @return {Boolean}
22397 hasSelection : function(){
22398 return this.selections.length > 0;
22402 * Returns True if the specified row is selected.
22403 * @param {Number/Record} record The record or index of the record to check
22404 * @return {Boolean}
22406 isSelected : function(index){
22407 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22408 return (r && this.selections.key(r.id) ? true : false);
22412 * Returns True if the specified record id is selected.
22413 * @param {String} id The id of record to check
22414 * @return {Boolean}
22416 isIdSelected : function(id){
22417 return (this.selections.key(id) ? true : false);
22421 handleMouseDown : function(e, t){
22422 var view = this.grid.getView(), rowIndex;
22423 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22426 if(e.shiftKey && this.last !== false){
22427 var last = this.last;
22428 this.selectRange(last, rowIndex, e.ctrlKey);
22429 this.last = last; // reset the last
22430 view.focusRow(rowIndex);
22432 var isSelected = this.isSelected(rowIndex);
22433 if(e.button !== 0 && isSelected){
22434 view.focusRow(rowIndex);
22435 }else if(e.ctrlKey && isSelected){
22436 this.deselectRow(rowIndex);
22437 }else if(!isSelected){
22438 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22439 view.focusRow(rowIndex);
22442 this.fireEvent("afterselectionchange", this);
22445 handleDragableRowClick : function(grid, rowIndex, e)
22447 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22448 this.selectRow(rowIndex, false);
22449 grid.view.focusRow(rowIndex);
22450 this.fireEvent("afterselectionchange", this);
22455 * Selects multiple rows.
22456 * @param {Array} rows Array of the indexes of the row to select
22457 * @param {Boolean} keepExisting (optional) True to keep existing selections
22459 selectRows : function(rows, keepExisting){
22461 this.clearSelections();
22463 for(var i = 0, len = rows.length; i < len; i++){
22464 this.selectRow(rows[i], true);
22469 * Selects a range of rows. All rows in between startRow and endRow are also selected.
22470 * @param {Number} startRow The index of the first row in the range
22471 * @param {Number} endRow The index of the last row in the range
22472 * @param {Boolean} keepExisting (optional) True to retain existing selections
22474 selectRange : function(startRow, endRow, keepExisting){
22479 this.clearSelections();
22481 if(startRow <= endRow){
22482 for(var i = startRow; i <= endRow; i++){
22483 this.selectRow(i, true);
22486 for(var i = startRow; i >= endRow; i--){
22487 this.selectRow(i, true);
22493 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22494 * @param {Number} startRow The index of the first row in the range
22495 * @param {Number} endRow The index of the last row in the range
22497 deselectRange : function(startRow, endRow, preventViewNotify){
22501 for(var i = startRow; i <= endRow; i++){
22502 this.deselectRow(i, preventViewNotify);
22508 * @param {Number} row The index of the row to select
22509 * @param {Boolean} keepExisting (optional) True to keep existing selections
22511 selectRow : function(index, keepExisting, preventViewNotify){
22512 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22515 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22516 if(!keepExisting || this.singleSelect){
22517 this.clearSelections();
22519 var r = this.grid.dataSource.getAt(index);
22520 this.selections.add(r);
22521 this.last = this.lastActive = index;
22522 if(!preventViewNotify){
22523 this.grid.getView().onRowSelect(index);
22525 this.fireEvent("rowselect", this, index, r);
22526 this.fireEvent("selectionchange", this);
22532 * @param {Number} row The index of the row to deselect
22534 deselectRow : function(index, preventViewNotify){
22538 if(this.last == index){
22541 if(this.lastActive == index){
22542 this.lastActive = false;
22544 var r = this.grid.dataSource.getAt(index);
22545 this.selections.remove(r);
22546 if(!preventViewNotify){
22547 this.grid.getView().onRowDeselect(index);
22549 this.fireEvent("rowdeselect", this, index);
22550 this.fireEvent("selectionchange", this);
22554 restoreLast : function(){
22556 this.last = this._last;
22561 acceptsNav : function(row, col, cm){
22562 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22566 onEditorKey : function(field, e){
22567 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22572 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22574 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22576 }else if(k == e.ENTER && !e.ctrlKey){
22580 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22582 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22584 }else if(k == e.ESC){
22588 g.startEditing(newCell[0], newCell[1]);
22593 * Ext JS Library 1.1.1
22594 * Copyright(c) 2006-2007, Ext JS, LLC.
22596 * Originally Released Under LGPL - original licence link has changed is not relivant.
22599 * <script type="text/javascript">
22603 * @class Roo.bootstrap.PagingToolbar
22604 * @extends Roo.bootstrap.NavSimplebar
22605 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22607 * Create a new PagingToolbar
22608 * @param {Object} config The config object
22609 * @param {Roo.data.Store} store
22611 Roo.bootstrap.PagingToolbar = function(config)
22613 // old args format still supported... - xtype is prefered..
22614 // created from xtype...
22616 this.ds = config.dataSource;
22618 if (config.store && !this.ds) {
22619 this.store= Roo.factory(config.store, Roo.data);
22620 this.ds = this.store;
22621 this.ds.xmodule = this.xmodule || false;
22624 this.toolbarItems = [];
22625 if (config.items) {
22626 this.toolbarItems = config.items;
22629 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22634 this.bind(this.ds);
22637 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22641 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22643 * @cfg {Roo.data.Store} dataSource
22644 * The underlying data store providing the paged data
22647 * @cfg {String/HTMLElement/Element} container
22648 * container The id or element that will contain the toolbar
22651 * @cfg {Boolean} displayInfo
22652 * True to display the displayMsg (defaults to false)
22655 * @cfg {Number} pageSize
22656 * The number of records to display per page (defaults to 20)
22660 * @cfg {String} displayMsg
22661 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22663 displayMsg : 'Displaying {0} - {1} of {2}',
22665 * @cfg {String} emptyMsg
22666 * The message to display when no records are found (defaults to "No data to display")
22668 emptyMsg : 'No data to display',
22670 * Customizable piece of the default paging text (defaults to "Page")
22673 beforePageText : "Page",
22675 * Customizable piece of the default paging text (defaults to "of %0")
22678 afterPageText : "of {0}",
22680 * Customizable piece of the default paging text (defaults to "First Page")
22683 firstText : "First Page",
22685 * Customizable piece of the default paging text (defaults to "Previous Page")
22688 prevText : "Previous Page",
22690 * Customizable piece of the default paging text (defaults to "Next Page")
22693 nextText : "Next Page",
22695 * Customizable piece of the default paging text (defaults to "Last Page")
22698 lastText : "Last Page",
22700 * Customizable piece of the default paging text (defaults to "Refresh")
22703 refreshText : "Refresh",
22707 onRender : function(ct, position)
22709 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22710 this.navgroup.parentId = this.id;
22711 this.navgroup.onRender(this.el, null);
22712 // add the buttons to the navgroup
22714 if(this.displayInfo){
22715 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22716 this.displayEl = this.el.select('.x-paging-info', true).first();
22717 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22718 // this.displayEl = navel.el.select('span',true).first();
22724 Roo.each(_this.buttons, function(e){ // this might need to use render????
22725 Roo.factory(e).onRender(_this.el, null);
22729 Roo.each(_this.toolbarItems, function(e) {
22730 _this.navgroup.addItem(e);
22734 this.first = this.navgroup.addItem({
22735 tooltip: this.firstText,
22737 icon : 'fa fa-backward',
22739 preventDefault: true,
22740 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22743 this.prev = this.navgroup.addItem({
22744 tooltip: this.prevText,
22746 icon : 'fa fa-step-backward',
22748 preventDefault: true,
22749 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22751 //this.addSeparator();
22754 var field = this.navgroup.addItem( {
22756 cls : 'x-paging-position',
22758 html : this.beforePageText +
22759 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22760 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22763 this.field = field.el.select('input', true).first();
22764 this.field.on("keydown", this.onPagingKeydown, this);
22765 this.field.on("focus", function(){this.dom.select();});
22768 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22769 //this.field.setHeight(18);
22770 //this.addSeparator();
22771 this.next = this.navgroup.addItem({
22772 tooltip: this.nextText,
22774 html : ' <i class="fa fa-step-forward">',
22776 preventDefault: true,
22777 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22779 this.last = this.navgroup.addItem({
22780 tooltip: this.lastText,
22781 icon : 'fa fa-forward',
22784 preventDefault: true,
22785 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22787 //this.addSeparator();
22788 this.loading = this.navgroup.addItem({
22789 tooltip: this.refreshText,
22790 icon: 'fa fa-refresh',
22791 preventDefault: true,
22792 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22798 updateInfo : function(){
22799 if(this.displayEl){
22800 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22801 var msg = count == 0 ?
22805 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22807 this.displayEl.update(msg);
22812 onLoad : function(ds, r, o){
22813 this.cursor = o.params ? o.params.start : 0;
22814 var d = this.getPageData(),
22818 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22819 this.field.dom.value = ap;
22820 this.first.setDisabled(ap == 1);
22821 this.prev.setDisabled(ap == 1);
22822 this.next.setDisabled(ap == ps);
22823 this.last.setDisabled(ap == ps);
22824 this.loading.enable();
22829 getPageData : function(){
22830 var total = this.ds.getTotalCount();
22833 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22834 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22839 onLoadError : function(){
22840 this.loading.enable();
22844 onPagingKeydown : function(e){
22845 var k = e.getKey();
22846 var d = this.getPageData();
22848 var v = this.field.dom.value, pageNum;
22849 if(!v || isNaN(pageNum = parseInt(v, 10))){
22850 this.field.dom.value = d.activePage;
22853 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22854 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22857 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))
22859 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22860 this.field.dom.value = pageNum;
22861 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22864 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22866 var v = this.field.dom.value, pageNum;
22867 var increment = (e.shiftKey) ? 10 : 1;
22868 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22871 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22872 this.field.dom.value = d.activePage;
22875 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22877 this.field.dom.value = parseInt(v, 10) + increment;
22878 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22879 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22886 beforeLoad : function(){
22888 this.loading.disable();
22893 onClick : function(which){
22902 ds.load({params:{start: 0, limit: this.pageSize}});
22905 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22908 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22911 var total = ds.getTotalCount();
22912 var extra = total % this.pageSize;
22913 var lastStart = extra ? (total - extra) : total-this.pageSize;
22914 ds.load({params:{start: lastStart, limit: this.pageSize}});
22917 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22923 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22924 * @param {Roo.data.Store} store The data store to unbind
22926 unbind : function(ds){
22927 ds.un("beforeload", this.beforeLoad, this);
22928 ds.un("load", this.onLoad, this);
22929 ds.un("loadexception", this.onLoadError, this);
22930 ds.un("remove", this.updateInfo, this);
22931 ds.un("add", this.updateInfo, this);
22932 this.ds = undefined;
22936 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22937 * @param {Roo.data.Store} store The data store to bind
22939 bind : function(ds){
22940 ds.on("beforeload", this.beforeLoad, this);
22941 ds.on("load", this.onLoad, this);
22942 ds.on("loadexception", this.onLoadError, this);
22943 ds.on("remove", this.updateInfo, this);
22944 ds.on("add", this.updateInfo, this);
22955 * @class Roo.bootstrap.MessageBar
22956 * @extends Roo.bootstrap.Component
22957 * Bootstrap MessageBar class
22958 * @cfg {String} html contents of the MessageBar
22959 * @cfg {String} weight (info | success | warning | danger) default info
22960 * @cfg {String} beforeClass insert the bar before the given class
22961 * @cfg {Boolean} closable (true | false) default false
22962 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22965 * Create a new Element
22966 * @param {Object} config The config object
22969 Roo.bootstrap.MessageBar = function(config){
22970 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22973 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22979 beforeClass: 'bootstrap-sticky-wrap',
22981 getAutoCreate : function(){
22985 cls: 'alert alert-dismissable alert-' + this.weight,
22990 html: this.html || ''
22996 cfg.cls += ' alert-messages-fixed';
23010 onRender : function(ct, position)
23012 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23015 var cfg = Roo.apply({}, this.getAutoCreate());
23019 cfg.cls += ' ' + this.cls;
23022 cfg.style = this.style;
23024 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23026 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23029 this.el.select('>button.close').on('click', this.hide, this);
23035 if (!this.rendered) {
23041 this.fireEvent('show', this);
23047 if (!this.rendered) {
23053 this.fireEvent('hide', this);
23056 update : function()
23058 // var e = this.el.dom.firstChild;
23060 // if(this.closable){
23061 // e = e.nextSibling;
23064 // e.data = this.html || '';
23066 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23082 * @class Roo.bootstrap.Graph
23083 * @extends Roo.bootstrap.Component
23084 * Bootstrap Graph class
23088 @cfg {String} graphtype bar | vbar | pie
23089 @cfg {number} g_x coodinator | centre x (pie)
23090 @cfg {number} g_y coodinator | centre y (pie)
23091 @cfg {number} g_r radius (pie)
23092 @cfg {number} g_height height of the chart (respected by all elements in the set)
23093 @cfg {number} g_width width of the chart (respected by all elements in the set)
23094 @cfg {Object} title The title of the chart
23097 -opts (object) options for the chart
23099 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23100 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23102 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.
23103 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23105 o stretch (boolean)
23107 -opts (object) options for the pie
23110 o startAngle (number)
23111 o endAngle (number)
23115 * Create a new Input
23116 * @param {Object} config The config object
23119 Roo.bootstrap.Graph = function(config){
23120 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23126 * The img click event for the img.
23127 * @param {Roo.EventObject} e
23133 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23144 //g_colors: this.colors,
23151 getAutoCreate : function(){
23162 onRender : function(ct,position){
23165 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23167 if (typeof(Raphael) == 'undefined') {
23168 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23172 this.raphael = Raphael(this.el.dom);
23174 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23175 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23176 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23177 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23179 r.text(160, 10, "Single Series Chart").attr(txtattr);
23180 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23181 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23182 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23184 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23185 r.barchart(330, 10, 300, 220, data1);
23186 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23187 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23190 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23191 // r.barchart(30, 30, 560, 250, xdata, {
23192 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23193 // axis : "0 0 1 1",
23194 // axisxlabels : xdata
23195 // //yvalues : cols,
23198 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23200 // this.load(null,xdata,{
23201 // axis : "0 0 1 1",
23202 // axisxlabels : xdata
23207 load : function(graphtype,xdata,opts)
23209 this.raphael.clear();
23211 graphtype = this.graphtype;
23216 var r = this.raphael,
23217 fin = function () {
23218 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23220 fout = function () {
23221 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23223 pfin = function() {
23224 this.sector.stop();
23225 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23228 this.label[0].stop();
23229 this.label[0].attr({ r: 7.5 });
23230 this.label[1].attr({ "font-weight": 800 });
23233 pfout = function() {
23234 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23237 this.label[0].animate({ r: 5 }, 500, "bounce");
23238 this.label[1].attr({ "font-weight": 400 });
23244 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23247 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23250 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23251 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23253 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23260 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23265 setTitle: function(o)
23270 initEvents: function() {
23273 this.el.on('click', this.onClick, this);
23277 onClick : function(e)
23279 Roo.log('img onclick');
23280 this.fireEvent('click', this, e);
23292 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23295 * @class Roo.bootstrap.dash.NumberBox
23296 * @extends Roo.bootstrap.Component
23297 * Bootstrap NumberBox class
23298 * @cfg {String} headline Box headline
23299 * @cfg {String} content Box content
23300 * @cfg {String} icon Box icon
23301 * @cfg {String} footer Footer text
23302 * @cfg {String} fhref Footer href
23305 * Create a new NumberBox
23306 * @param {Object} config The config object
23310 Roo.bootstrap.dash.NumberBox = function(config){
23311 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23315 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23324 getAutoCreate : function(){
23328 cls : 'small-box ',
23336 cls : 'roo-headline',
23337 html : this.headline
23341 cls : 'roo-content',
23342 html : this.content
23356 cls : 'ion ' + this.icon
23365 cls : 'small-box-footer',
23366 href : this.fhref || '#',
23370 cfg.cn.push(footer);
23377 onRender : function(ct,position){
23378 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23385 setHeadline: function (value)
23387 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23390 setFooter: function (value, href)
23392 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23395 this.el.select('a.small-box-footer',true).first().attr('href', href);
23400 setContent: function (value)
23402 this.el.select('.roo-content',true).first().dom.innerHTML = value;
23405 initEvents: function()
23419 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23422 * @class Roo.bootstrap.dash.TabBox
23423 * @extends Roo.bootstrap.Component
23424 * Bootstrap TabBox class
23425 * @cfg {String} title Title of the TabBox
23426 * @cfg {String} icon Icon of the TabBox
23427 * @cfg {Boolean} showtabs (true|false) show the tabs default true
23428 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23431 * Create a new TabBox
23432 * @param {Object} config The config object
23436 Roo.bootstrap.dash.TabBox = function(config){
23437 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23442 * When a pane is added
23443 * @param {Roo.bootstrap.dash.TabPane} pane
23447 * @event activatepane
23448 * When a pane is activated
23449 * @param {Roo.bootstrap.dash.TabPane} pane
23451 "activatepane" : true
23459 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
23464 tabScrollable : false,
23466 getChildContainer : function()
23468 return this.el.select('.tab-content', true).first();
23471 getAutoCreate : function(){
23475 cls: 'pull-left header',
23483 cls: 'fa ' + this.icon
23489 cls: 'nav nav-tabs pull-right',
23495 if(this.tabScrollable){
23502 cls: 'nav nav-tabs pull-right',
23513 cls: 'nav-tabs-custom',
23518 cls: 'tab-content no-padding',
23526 initEvents : function()
23528 //Roo.log('add add pane handler');
23529 this.on('addpane', this.onAddPane, this);
23532 * Updates the box title
23533 * @param {String} html to set the title to.
23535 setTitle : function(value)
23537 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23539 onAddPane : function(pane)
23541 this.panes.push(pane);
23542 //Roo.log('addpane');
23544 // tabs are rendere left to right..
23545 if(!this.showtabs){
23549 var ctr = this.el.select('.nav-tabs', true).first();
23552 var existing = ctr.select('.nav-tab',true);
23553 var qty = existing.getCount();;
23556 var tab = ctr.createChild({
23558 cls : 'nav-tab' + (qty ? '' : ' active'),
23566 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23569 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23571 pane.el.addClass('active');
23576 onTabClick : function(ev,un,ob,pane)
23578 //Roo.log('tab - prev default');
23579 ev.preventDefault();
23582 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23583 pane.tab.addClass('active');
23584 //Roo.log(pane.title);
23585 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23586 // technically we should have a deactivate event.. but maybe add later.
23587 // and it should not de-activate the selected tab...
23588 this.fireEvent('activatepane', pane);
23589 pane.el.addClass('active');
23590 pane.fireEvent('activate');
23595 getActivePane : function()
23598 Roo.each(this.panes, function(p) {
23599 if(p.el.hasClass('active')){
23620 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23622 * @class Roo.bootstrap.TabPane
23623 * @extends Roo.bootstrap.Component
23624 * Bootstrap TabPane class
23625 * @cfg {Boolean} active (false | true) Default false
23626 * @cfg {String} title title of panel
23630 * Create a new TabPane
23631 * @param {Object} config The config object
23634 Roo.bootstrap.dash.TabPane = function(config){
23635 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23641 * When a pane is activated
23642 * @param {Roo.bootstrap.dash.TabPane} pane
23649 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23654 // the tabBox that this is attached to.
23657 getAutoCreate : function()
23665 cfg.cls += ' active';
23670 initEvents : function()
23672 //Roo.log('trigger add pane handler');
23673 this.parent().fireEvent('addpane', this)
23677 * Updates the tab title
23678 * @param {String} html to set the title to.
23680 setTitle: function(str)
23686 this.tab.select('a', true).first().dom.innerHTML = str;
23703 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23706 * @class Roo.bootstrap.menu.Menu
23707 * @extends Roo.bootstrap.Component
23708 * Bootstrap Menu class - container for Menu
23709 * @cfg {String} html Text of the menu
23710 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23711 * @cfg {String} icon Font awesome icon
23712 * @cfg {String} pos Menu align to (top | bottom) default bottom
23716 * Create a new Menu
23717 * @param {Object} config The config object
23721 Roo.bootstrap.menu.Menu = function(config){
23722 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23726 * @event beforeshow
23727 * Fires before this menu is displayed
23728 * @param {Roo.bootstrap.menu.Menu} this
23732 * @event beforehide
23733 * Fires before this menu is hidden
23734 * @param {Roo.bootstrap.menu.Menu} this
23739 * Fires after this menu is displayed
23740 * @param {Roo.bootstrap.menu.Menu} this
23745 * Fires after this menu is hidden
23746 * @param {Roo.bootstrap.menu.Menu} this
23751 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23752 * @param {Roo.bootstrap.menu.Menu} this
23753 * @param {Roo.EventObject} e
23760 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23764 weight : 'default',
23769 getChildContainer : function() {
23770 if(this.isSubMenu){
23774 return this.el.select('ul.dropdown-menu', true).first();
23777 getAutoCreate : function()
23782 cls : 'roo-menu-text',
23790 cls : 'fa ' + this.icon
23801 cls : 'dropdown-button btn btn-' + this.weight,
23806 cls : 'dropdown-toggle btn btn-' + this.weight,
23816 cls : 'dropdown-menu'
23822 if(this.pos == 'top'){
23823 cfg.cls += ' dropup';
23826 if(this.isSubMenu){
23829 cls : 'dropdown-menu'
23836 onRender : function(ct, position)
23838 this.isSubMenu = ct.hasClass('dropdown-submenu');
23840 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23843 initEvents : function()
23845 if(this.isSubMenu){
23849 this.hidden = true;
23851 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23852 this.triggerEl.on('click', this.onTriggerPress, this);
23854 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23855 this.buttonEl.on('click', this.onClick, this);
23861 if(this.isSubMenu){
23865 return this.el.select('ul.dropdown-menu', true).first();
23868 onClick : function(e)
23870 this.fireEvent("click", this, e);
23873 onTriggerPress : function(e)
23875 if (this.isVisible()) {
23882 isVisible : function(){
23883 return !this.hidden;
23888 this.fireEvent("beforeshow", this);
23890 this.hidden = false;
23891 this.el.addClass('open');
23893 Roo.get(document).on("mouseup", this.onMouseUp, this);
23895 this.fireEvent("show", this);
23902 this.fireEvent("beforehide", this);
23904 this.hidden = true;
23905 this.el.removeClass('open');
23907 Roo.get(document).un("mouseup", this.onMouseUp);
23909 this.fireEvent("hide", this);
23912 onMouseUp : function()
23926 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23929 * @class Roo.bootstrap.menu.Item
23930 * @extends Roo.bootstrap.Component
23931 * Bootstrap MenuItem class
23932 * @cfg {Boolean} submenu (true | false) default false
23933 * @cfg {String} html text of the item
23934 * @cfg {String} href the link
23935 * @cfg {Boolean} disable (true | false) default false
23936 * @cfg {Boolean} preventDefault (true | false) default true
23937 * @cfg {String} icon Font awesome icon
23938 * @cfg {String} pos Submenu align to (left | right) default right
23942 * Create a new Item
23943 * @param {Object} config The config object
23947 Roo.bootstrap.menu.Item = function(config){
23948 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23952 * Fires when the mouse is hovering over this menu
23953 * @param {Roo.bootstrap.menu.Item} this
23954 * @param {Roo.EventObject} e
23959 * Fires when the mouse exits this menu
23960 * @param {Roo.bootstrap.menu.Item} this
23961 * @param {Roo.EventObject} e
23967 * The raw click event for the entire grid.
23968 * @param {Roo.EventObject} e
23974 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23979 preventDefault: true,
23984 getAutoCreate : function()
23989 cls : 'roo-menu-item-text',
23997 cls : 'fa ' + this.icon
24006 href : this.href || '#',
24013 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24017 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24019 if(this.pos == 'left'){
24020 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24027 initEvents : function()
24029 this.el.on('mouseover', this.onMouseOver, this);
24030 this.el.on('mouseout', this.onMouseOut, this);
24032 this.el.select('a', true).first().on('click', this.onClick, this);
24036 onClick : function(e)
24038 if(this.preventDefault){
24039 e.preventDefault();
24042 this.fireEvent("click", this, e);
24045 onMouseOver : function(e)
24047 if(this.submenu && this.pos == 'left'){
24048 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24051 this.fireEvent("mouseover", this, e);
24054 onMouseOut : function(e)
24056 this.fireEvent("mouseout", this, e);
24068 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24071 * @class Roo.bootstrap.menu.Separator
24072 * @extends Roo.bootstrap.Component
24073 * Bootstrap Separator class
24076 * Create a new Separator
24077 * @param {Object} config The config object
24081 Roo.bootstrap.menu.Separator = function(config){
24082 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24085 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24087 getAutoCreate : function(){
24108 * @class Roo.bootstrap.Tooltip
24109 * Bootstrap Tooltip class
24110 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24111 * to determine which dom element triggers the tooltip.
24113 * It needs to add support for additional attributes like tooltip-position
24116 * Create a new Toolti
24117 * @param {Object} config The config object
24120 Roo.bootstrap.Tooltip = function(config){
24121 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24124 Roo.apply(Roo.bootstrap.Tooltip, {
24126 * @function init initialize tooltip monitoring.
24130 currentTip : false,
24131 currentRegion : false,
24137 Roo.get(document).on('mouseover', this.enter ,this);
24138 Roo.get(document).on('mouseout', this.leave, this);
24141 this.currentTip = new Roo.bootstrap.Tooltip();
24144 enter : function(ev)
24146 var dom = ev.getTarget();
24148 //Roo.log(['enter',dom]);
24149 var el = Roo.fly(dom);
24150 if (this.currentEl) {
24152 //Roo.log(this.currentEl);
24153 //Roo.log(this.currentEl.contains(dom));
24154 if (this.currentEl == el) {
24157 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24163 if (this.currentTip.el) {
24164 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24168 if(!el || el.dom == document){
24174 // you can not look for children, as if el is the body.. then everythign is the child..
24175 if (!el.attr('tooltip')) { //
24176 if (!el.select("[tooltip]").elements.length) {
24179 // is the mouse over this child...?
24180 bindEl = el.select("[tooltip]").first();
24181 var xy = ev.getXY();
24182 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24183 //Roo.log("not in region.");
24186 //Roo.log("child element over..");
24189 this.currentEl = bindEl;
24190 this.currentTip.bind(bindEl);
24191 this.currentRegion = Roo.lib.Region.getRegion(dom);
24192 this.currentTip.enter();
24195 leave : function(ev)
24197 var dom = ev.getTarget();
24198 //Roo.log(['leave',dom]);
24199 if (!this.currentEl) {
24204 if (dom != this.currentEl.dom) {
24207 var xy = ev.getXY();
24208 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24211 // only activate leave if mouse cursor is outside... bounding box..
24216 if (this.currentTip) {
24217 this.currentTip.leave();
24219 //Roo.log('clear currentEl');
24220 this.currentEl = false;
24225 'left' : ['r-l', [-2,0], 'right'],
24226 'right' : ['l-r', [2,0], 'left'],
24227 'bottom' : ['t-b', [0,2], 'top'],
24228 'top' : [ 'b-t', [0,-2], 'bottom']
24234 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24239 delay : null, // can be { show : 300 , hide: 500}
24243 hoverState : null, //???
24245 placement : 'bottom',
24247 getAutoCreate : function(){
24254 cls : 'tooltip-arrow'
24257 cls : 'tooltip-inner'
24264 bind : function(el)
24270 enter : function () {
24272 if (this.timeout != null) {
24273 clearTimeout(this.timeout);
24276 this.hoverState = 'in';
24277 //Roo.log("enter - show");
24278 if (!this.delay || !this.delay.show) {
24283 this.timeout = setTimeout(function () {
24284 if (_t.hoverState == 'in') {
24287 }, this.delay.show);
24291 clearTimeout(this.timeout);
24293 this.hoverState = 'out';
24294 if (!this.delay || !this.delay.hide) {
24300 this.timeout = setTimeout(function () {
24301 //Roo.log("leave - timeout");
24303 if (_t.hoverState == 'out') {
24305 Roo.bootstrap.Tooltip.currentEl = false;
24313 this.render(document.body);
24316 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24318 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24320 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24322 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24324 var placement = typeof this.placement == 'function' ?
24325 this.placement.call(this, this.el, on_el) :
24328 var autoToken = /\s?auto?\s?/i;
24329 var autoPlace = autoToken.test(placement);
24331 placement = placement.replace(autoToken, '') || 'top';
24335 //this.el.setXY([0,0]);
24337 //this.el.dom.style.display='block';
24339 //this.el.appendTo(on_el);
24341 var p = this.getPosition();
24342 var box = this.el.getBox();
24348 var align = Roo.bootstrap.Tooltip.alignment[placement];
24350 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24352 if(placement == 'top' || placement == 'bottom'){
24354 placement = 'right';
24357 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24358 placement = 'left';
24361 var scroll = Roo.select('body', true).first().getScroll();
24363 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24369 align = Roo.bootstrap.Tooltip.alignment[placement];
24371 this.el.alignTo(this.bindEl, align[0],align[1]);
24372 //var arrow = this.el.select('.arrow',true).first();
24373 //arrow.set(align[2],
24375 this.el.addClass(placement);
24377 this.el.addClass('in fade');
24379 this.hoverState = null;
24381 if (this.el.hasClass('fade')) {
24392 //this.el.setXY([0,0]);
24393 this.el.removeClass('in');
24409 * @class Roo.bootstrap.LocationPicker
24410 * @extends Roo.bootstrap.Component
24411 * Bootstrap LocationPicker class
24412 * @cfg {Number} latitude Position when init default 0
24413 * @cfg {Number} longitude Position when init default 0
24414 * @cfg {Number} zoom default 15
24415 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24416 * @cfg {Boolean} mapTypeControl default false
24417 * @cfg {Boolean} disableDoubleClickZoom default false
24418 * @cfg {Boolean} scrollwheel default true
24419 * @cfg {Boolean} streetViewControl default false
24420 * @cfg {Number} radius default 0
24421 * @cfg {String} locationName
24422 * @cfg {Boolean} draggable default true
24423 * @cfg {Boolean} enableAutocomplete default false
24424 * @cfg {Boolean} enableReverseGeocode default true
24425 * @cfg {String} markerTitle
24428 * Create a new LocationPicker
24429 * @param {Object} config The config object
24433 Roo.bootstrap.LocationPicker = function(config){
24435 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24440 * Fires when the picker initialized.
24441 * @param {Roo.bootstrap.LocationPicker} this
24442 * @param {Google Location} location
24446 * @event positionchanged
24447 * Fires when the picker position changed.
24448 * @param {Roo.bootstrap.LocationPicker} this
24449 * @param {Google Location} location
24451 positionchanged : true,
24454 * Fires when the map resize.
24455 * @param {Roo.bootstrap.LocationPicker} this
24460 * Fires when the map show.
24461 * @param {Roo.bootstrap.LocationPicker} this
24466 * Fires when the map hide.
24467 * @param {Roo.bootstrap.LocationPicker} this
24472 * Fires when click the map.
24473 * @param {Roo.bootstrap.LocationPicker} this
24474 * @param {Map event} e
24478 * @event mapRightClick
24479 * Fires when right click the map.
24480 * @param {Roo.bootstrap.LocationPicker} this
24481 * @param {Map event} e
24483 mapRightClick : true,
24485 * @event markerClick
24486 * Fires when click the marker.
24487 * @param {Roo.bootstrap.LocationPicker} this
24488 * @param {Map event} e
24490 markerClick : true,
24492 * @event markerRightClick
24493 * Fires when right click the marker.
24494 * @param {Roo.bootstrap.LocationPicker} this
24495 * @param {Map event} e
24497 markerRightClick : true,
24499 * @event OverlayViewDraw
24500 * Fires when OverlayView Draw
24501 * @param {Roo.bootstrap.LocationPicker} this
24503 OverlayViewDraw : true,
24505 * @event OverlayViewOnAdd
24506 * Fires when OverlayView Draw
24507 * @param {Roo.bootstrap.LocationPicker} this
24509 OverlayViewOnAdd : true,
24511 * @event OverlayViewOnRemove
24512 * Fires when OverlayView Draw
24513 * @param {Roo.bootstrap.LocationPicker} this
24515 OverlayViewOnRemove : true,
24517 * @event OverlayViewShow
24518 * Fires when OverlayView Draw
24519 * @param {Roo.bootstrap.LocationPicker} this
24520 * @param {Pixel} cpx
24522 OverlayViewShow : true,
24524 * @event OverlayViewHide
24525 * Fires when OverlayView Draw
24526 * @param {Roo.bootstrap.LocationPicker} this
24528 OverlayViewHide : true,
24530 * @event loadexception
24531 * Fires when load google lib failed.
24532 * @param {Roo.bootstrap.LocationPicker} this
24534 loadexception : true
24539 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24541 gMapContext: false,
24547 mapTypeControl: false,
24548 disableDoubleClickZoom: false,
24550 streetViewControl: false,
24554 enableAutocomplete: false,
24555 enableReverseGeocode: true,
24558 getAutoCreate: function()
24563 cls: 'roo-location-picker'
24569 initEvents: function(ct, position)
24571 if(!this.el.getWidth() || this.isApplied()){
24575 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24580 initial: function()
24582 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24583 this.fireEvent('loadexception', this);
24587 if(!this.mapTypeId){
24588 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24591 this.gMapContext = this.GMapContext();
24593 this.initOverlayView();
24595 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24599 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24600 _this.setPosition(_this.gMapContext.marker.position);
24603 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24604 _this.fireEvent('mapClick', this, event);
24608 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24609 _this.fireEvent('mapRightClick', this, event);
24613 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24614 _this.fireEvent('markerClick', this, event);
24618 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24619 _this.fireEvent('markerRightClick', this, event);
24623 this.setPosition(this.gMapContext.location);
24625 this.fireEvent('initial', this, this.gMapContext.location);
24628 initOverlayView: function()
24632 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24636 _this.fireEvent('OverlayViewDraw', _this);
24641 _this.fireEvent('OverlayViewOnAdd', _this);
24644 onRemove: function()
24646 _this.fireEvent('OverlayViewOnRemove', _this);
24649 show: function(cpx)
24651 _this.fireEvent('OverlayViewShow', _this, cpx);
24656 _this.fireEvent('OverlayViewHide', _this);
24662 fromLatLngToContainerPixel: function(event)
24664 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24667 isApplied: function()
24669 return this.getGmapContext() == false ? false : true;
24672 getGmapContext: function()
24674 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24677 GMapContext: function()
24679 var position = new google.maps.LatLng(this.latitude, this.longitude);
24681 var _map = new google.maps.Map(this.el.dom, {
24684 mapTypeId: this.mapTypeId,
24685 mapTypeControl: this.mapTypeControl,
24686 disableDoubleClickZoom: this.disableDoubleClickZoom,
24687 scrollwheel: this.scrollwheel,
24688 streetViewControl: this.streetViewControl,
24689 locationName: this.locationName,
24690 draggable: this.draggable,
24691 enableAutocomplete: this.enableAutocomplete,
24692 enableReverseGeocode: this.enableReverseGeocode
24695 var _marker = new google.maps.Marker({
24696 position: position,
24698 title: this.markerTitle,
24699 draggable: this.draggable
24706 location: position,
24707 radius: this.radius,
24708 locationName: this.locationName,
24709 addressComponents: {
24710 formatted_address: null,
24711 addressLine1: null,
24712 addressLine2: null,
24714 streetNumber: null,
24718 stateOrProvince: null
24721 domContainer: this.el.dom,
24722 geodecoder: new google.maps.Geocoder()
24726 drawCircle: function(center, radius, options)
24728 if (this.gMapContext.circle != null) {
24729 this.gMapContext.circle.setMap(null);
24733 options = Roo.apply({}, options, {
24734 strokeColor: "#0000FF",
24735 strokeOpacity: .35,
24737 fillColor: "#0000FF",
24741 options.map = this.gMapContext.map;
24742 options.radius = radius;
24743 options.center = center;
24744 this.gMapContext.circle = new google.maps.Circle(options);
24745 return this.gMapContext.circle;
24751 setPosition: function(location)
24753 this.gMapContext.location = location;
24754 this.gMapContext.marker.setPosition(location);
24755 this.gMapContext.map.panTo(location);
24756 this.drawCircle(location, this.gMapContext.radius, {});
24760 if (this.gMapContext.settings.enableReverseGeocode) {
24761 this.gMapContext.geodecoder.geocode({
24762 latLng: this.gMapContext.location
24763 }, function(results, status) {
24765 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24766 _this.gMapContext.locationName = results[0].formatted_address;
24767 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24769 _this.fireEvent('positionchanged', this, location);
24776 this.fireEvent('positionchanged', this, location);
24781 google.maps.event.trigger(this.gMapContext.map, "resize");
24783 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24785 this.fireEvent('resize', this);
24788 setPositionByLatLng: function(latitude, longitude)
24790 this.setPosition(new google.maps.LatLng(latitude, longitude));
24793 getCurrentPosition: function()
24796 latitude: this.gMapContext.location.lat(),
24797 longitude: this.gMapContext.location.lng()
24801 getAddressName: function()
24803 return this.gMapContext.locationName;
24806 getAddressComponents: function()
24808 return this.gMapContext.addressComponents;
24811 address_component_from_google_geocode: function(address_components)
24815 for (var i = 0; i < address_components.length; i++) {
24816 var component = address_components[i];
24817 if (component.types.indexOf("postal_code") >= 0) {
24818 result.postalCode = component.short_name;
24819 } else if (component.types.indexOf("street_number") >= 0) {
24820 result.streetNumber = component.short_name;
24821 } else if (component.types.indexOf("route") >= 0) {
24822 result.streetName = component.short_name;
24823 } else if (component.types.indexOf("neighborhood") >= 0) {
24824 result.city = component.short_name;
24825 } else if (component.types.indexOf("locality") >= 0) {
24826 result.city = component.short_name;
24827 } else if (component.types.indexOf("sublocality") >= 0) {
24828 result.district = component.short_name;
24829 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24830 result.stateOrProvince = component.short_name;
24831 } else if (component.types.indexOf("country") >= 0) {
24832 result.country = component.short_name;
24836 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24837 result.addressLine2 = "";
24841 setZoomLevel: function(zoom)
24843 this.gMapContext.map.setZoom(zoom);
24856 this.fireEvent('show', this);
24867 this.fireEvent('hide', this);
24872 Roo.apply(Roo.bootstrap.LocationPicker, {
24874 OverlayView : function(map, options)
24876 options = options || {};
24890 * @class Roo.bootstrap.Alert
24891 * @extends Roo.bootstrap.Component
24892 * Bootstrap Alert class
24893 * @cfg {String} title The title of alert
24894 * @cfg {String} html The content of alert
24895 * @cfg {String} weight ( success | info | warning | danger )
24896 * @cfg {String} faicon font-awesomeicon
24899 * Create a new alert
24900 * @param {Object} config The config object
24904 Roo.bootstrap.Alert = function(config){
24905 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24909 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24916 getAutoCreate : function()
24925 cls : 'roo-alert-icon'
24930 cls : 'roo-alert-title',
24935 cls : 'roo-alert-text',
24942 cfg.cn[0].cls += ' fa ' + this.faicon;
24946 cfg.cls += ' alert-' + this.weight;
24952 initEvents: function()
24954 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24957 setTitle : function(str)
24959 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24962 setText : function(str)
24964 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24967 setWeight : function(weight)
24970 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24973 this.weight = weight;
24975 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24978 setIcon : function(icon)
24981 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24984 this.faicon = icon;
24986 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25007 * @class Roo.bootstrap.UploadCropbox
25008 * @extends Roo.bootstrap.Component
25009 * Bootstrap UploadCropbox class
25010 * @cfg {String} emptyText show when image has been loaded
25011 * @cfg {String} rotateNotify show when image too small to rotate
25012 * @cfg {Number} errorTimeout default 3000
25013 * @cfg {Number} minWidth default 300
25014 * @cfg {Number} minHeight default 300
25015 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25016 * @cfg {Boolean} isDocument (true|false) default false
25017 * @cfg {String} url action url
25018 * @cfg {String} paramName default 'imageUpload'
25019 * @cfg {String} method default POST
25020 * @cfg {Boolean} loadMask (true|false) default true
25021 * @cfg {Boolean} loadingText default 'Loading...'
25024 * Create a new UploadCropbox
25025 * @param {Object} config The config object
25028 Roo.bootstrap.UploadCropbox = function(config){
25029 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25033 * @event beforeselectfile
25034 * Fire before select file
25035 * @param {Roo.bootstrap.UploadCropbox} this
25037 "beforeselectfile" : true,
25040 * Fire after initEvent
25041 * @param {Roo.bootstrap.UploadCropbox} this
25046 * Fire after initEvent
25047 * @param {Roo.bootstrap.UploadCropbox} this
25048 * @param {String} data
25053 * Fire when preparing the file data
25054 * @param {Roo.bootstrap.UploadCropbox} this
25055 * @param {Object} file
25060 * Fire when get exception
25061 * @param {Roo.bootstrap.UploadCropbox} this
25062 * @param {XMLHttpRequest} xhr
25064 "exception" : true,
25066 * @event beforeloadcanvas
25067 * Fire before load the canvas
25068 * @param {Roo.bootstrap.UploadCropbox} this
25069 * @param {String} src
25071 "beforeloadcanvas" : true,
25074 * Fire when trash image
25075 * @param {Roo.bootstrap.UploadCropbox} this
25080 * Fire when download the image
25081 * @param {Roo.bootstrap.UploadCropbox} this
25085 * @event footerbuttonclick
25086 * Fire when footerbuttonclick
25087 * @param {Roo.bootstrap.UploadCropbox} this
25088 * @param {String} type
25090 "footerbuttonclick" : true,
25094 * @param {Roo.bootstrap.UploadCropbox} this
25099 * Fire when rotate the image
25100 * @param {Roo.bootstrap.UploadCropbox} this
25101 * @param {String} pos
25106 * Fire when inspect the file
25107 * @param {Roo.bootstrap.UploadCropbox} this
25108 * @param {Object} file
25113 * Fire when xhr upload the file
25114 * @param {Roo.bootstrap.UploadCropbox} this
25115 * @param {Object} data
25120 * Fire when arrange the file data
25121 * @param {Roo.bootstrap.UploadCropbox} this
25122 * @param {Object} formData
25127 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25130 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25132 emptyText : 'Click to upload image',
25133 rotateNotify : 'Image is too small to rotate',
25134 errorTimeout : 3000,
25148 cropType : 'image/jpeg',
25150 canvasLoaded : false,
25151 isDocument : false,
25153 paramName : 'imageUpload',
25155 loadingText : 'Loading...',
25158 getAutoCreate : function()
25162 cls : 'roo-upload-cropbox',
25166 cls : 'roo-upload-cropbox-selector',
25171 cls : 'roo-upload-cropbox-body',
25172 style : 'cursor:pointer',
25176 cls : 'roo-upload-cropbox-preview'
25180 cls : 'roo-upload-cropbox-thumb'
25184 cls : 'roo-upload-cropbox-empty-notify',
25185 html : this.emptyText
25189 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25190 html : this.rotateNotify
25196 cls : 'roo-upload-cropbox-footer',
25199 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25209 onRender : function(ct, position)
25211 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25213 if (this.buttons.length) {
25215 Roo.each(this.buttons, function(bb) {
25217 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25219 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25225 this.maskEl = this.el;
25229 initEvents : function()
25231 this.urlAPI = (window.createObjectURL && window) ||
25232 (window.URL && URL.revokeObjectURL && URL) ||
25233 (window.webkitURL && webkitURL);
25235 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25236 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25238 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25239 this.selectorEl.hide();
25241 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25242 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25244 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25245 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25246 this.thumbEl.hide();
25248 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25249 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25251 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25252 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25253 this.errorEl.hide();
25255 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25256 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25257 this.footerEl.hide();
25259 this.setThumbBoxSize();
25265 this.fireEvent('initial', this);
25272 window.addEventListener("resize", function() { _this.resize(); } );
25274 this.bodyEl.on('click', this.beforeSelectFile, this);
25277 this.bodyEl.on('touchstart', this.onTouchStart, this);
25278 this.bodyEl.on('touchmove', this.onTouchMove, this);
25279 this.bodyEl.on('touchend', this.onTouchEnd, this);
25283 this.bodyEl.on('mousedown', this.onMouseDown, this);
25284 this.bodyEl.on('mousemove', this.onMouseMove, this);
25285 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25286 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25287 Roo.get(document).on('mouseup', this.onMouseUp, this);
25290 this.selectorEl.on('change', this.onFileSelected, this);
25296 this.baseScale = 1;
25298 this.baseRotate = 1;
25299 this.dragable = false;
25300 this.pinching = false;
25303 this.cropData = false;
25304 this.notifyEl.dom.innerHTML = this.emptyText;
25306 this.selectorEl.dom.value = '';
25310 resize : function()
25312 if(this.fireEvent('resize', this) != false){
25313 this.setThumbBoxPosition();
25314 this.setCanvasPosition();
25318 onFooterButtonClick : function(e, el, o, type)
25321 case 'rotate-left' :
25322 this.onRotateLeft(e);
25324 case 'rotate-right' :
25325 this.onRotateRight(e);
25328 this.beforeSelectFile(e);
25343 this.fireEvent('footerbuttonclick', this, type);
25346 beforeSelectFile : function(e)
25348 e.preventDefault();
25350 if(this.fireEvent('beforeselectfile', this) != false){
25351 this.selectorEl.dom.click();
25355 onFileSelected : function(e)
25357 e.preventDefault();
25359 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25363 var file = this.selectorEl.dom.files[0];
25365 if(this.fireEvent('inspect', this, file) != false){
25366 this.prepare(file);
25371 trash : function(e)
25373 this.fireEvent('trash', this);
25376 download : function(e)
25378 this.fireEvent('download', this);
25381 loadCanvas : function(src)
25383 if(this.fireEvent('beforeloadcanvas', this, src) != false){
25387 this.imageEl = document.createElement('img');
25391 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25393 this.imageEl.src = src;
25397 onLoadCanvas : function()
25399 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25400 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25402 this.bodyEl.un('click', this.beforeSelectFile, this);
25404 this.notifyEl.hide();
25405 this.thumbEl.show();
25406 this.footerEl.show();
25408 this.baseRotateLevel();
25410 if(this.isDocument){
25411 this.setThumbBoxSize();
25414 this.setThumbBoxPosition();
25416 this.baseScaleLevel();
25422 this.canvasLoaded = true;
25425 this.maskEl.unmask();
25430 setCanvasPosition : function()
25432 if(!this.canvasEl){
25436 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25437 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25439 this.previewEl.setLeft(pw);
25440 this.previewEl.setTop(ph);
25444 onMouseDown : function(e)
25448 this.dragable = true;
25449 this.pinching = false;
25451 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25452 this.dragable = false;
25456 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25457 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25461 onMouseMove : function(e)
25465 if(!this.canvasLoaded){
25469 if (!this.dragable){
25473 var minX = Math.ceil(this.thumbEl.getLeft(true));
25474 var minY = Math.ceil(this.thumbEl.getTop(true));
25476 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25477 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25479 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25480 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25482 x = x - this.mouseX;
25483 y = y - this.mouseY;
25485 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25486 var bgY = Math.ceil(y + this.previewEl.getTop(true));
25488 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25489 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25491 this.previewEl.setLeft(bgX);
25492 this.previewEl.setTop(bgY);
25494 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25495 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25498 onMouseUp : function(e)
25502 this.dragable = false;
25505 onMouseWheel : function(e)
25509 this.startScale = this.scale;
25511 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25513 if(!this.zoomable()){
25514 this.scale = this.startScale;
25523 zoomable : function()
25525 var minScale = this.thumbEl.getWidth() / this.minWidth;
25527 if(this.minWidth < this.minHeight){
25528 minScale = this.thumbEl.getHeight() / this.minHeight;
25531 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25532 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25536 (this.rotate == 0 || this.rotate == 180) &&
25538 width > this.imageEl.OriginWidth ||
25539 height > this.imageEl.OriginHeight ||
25540 (width < this.minWidth && height < this.minHeight)
25548 (this.rotate == 90 || this.rotate == 270) &&
25550 width > this.imageEl.OriginWidth ||
25551 height > this.imageEl.OriginHeight ||
25552 (width < this.minHeight && height < this.minWidth)
25559 !this.isDocument &&
25560 (this.rotate == 0 || this.rotate == 180) &&
25562 width < this.minWidth ||
25563 width > this.imageEl.OriginWidth ||
25564 height < this.minHeight ||
25565 height > this.imageEl.OriginHeight
25572 !this.isDocument &&
25573 (this.rotate == 90 || this.rotate == 270) &&
25575 width < this.minHeight ||
25576 width > this.imageEl.OriginWidth ||
25577 height < this.minWidth ||
25578 height > this.imageEl.OriginHeight
25588 onRotateLeft : function(e)
25590 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25592 var minScale = this.thumbEl.getWidth() / this.minWidth;
25594 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25595 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25597 this.startScale = this.scale;
25599 while (this.getScaleLevel() < minScale){
25601 this.scale = this.scale + 1;
25603 if(!this.zoomable()){
25608 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25609 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25614 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25621 this.scale = this.startScale;
25623 this.onRotateFail();
25628 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25630 if(this.isDocument){
25631 this.setThumbBoxSize();
25632 this.setThumbBoxPosition();
25633 this.setCanvasPosition();
25638 this.fireEvent('rotate', this, 'left');
25642 onRotateRight : function(e)
25644 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25646 var minScale = this.thumbEl.getWidth() / this.minWidth;
25648 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25649 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25651 this.startScale = this.scale;
25653 while (this.getScaleLevel() < minScale){
25655 this.scale = this.scale + 1;
25657 if(!this.zoomable()){
25662 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25663 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25668 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25675 this.scale = this.startScale;
25677 this.onRotateFail();
25682 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25684 if(this.isDocument){
25685 this.setThumbBoxSize();
25686 this.setThumbBoxPosition();
25687 this.setCanvasPosition();
25692 this.fireEvent('rotate', this, 'right');
25695 onRotateFail : function()
25697 this.errorEl.show(true);
25701 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25706 this.previewEl.dom.innerHTML = '';
25708 var canvasEl = document.createElement("canvas");
25710 var contextEl = canvasEl.getContext("2d");
25712 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25713 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25714 var center = this.imageEl.OriginWidth / 2;
25716 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25717 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25718 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25719 center = this.imageEl.OriginHeight / 2;
25722 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25724 contextEl.translate(center, center);
25725 contextEl.rotate(this.rotate * Math.PI / 180);
25727 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25729 this.canvasEl = document.createElement("canvas");
25731 this.contextEl = this.canvasEl.getContext("2d");
25733 switch (this.rotate) {
25736 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25737 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25739 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25744 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25745 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25747 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25748 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);
25752 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25757 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25758 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25760 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25761 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);
25765 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);
25770 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25771 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25773 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25774 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25778 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);
25785 this.previewEl.appendChild(this.canvasEl);
25787 this.setCanvasPosition();
25792 if(!this.canvasLoaded){
25796 var imageCanvas = document.createElement("canvas");
25798 var imageContext = imageCanvas.getContext("2d");
25800 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25801 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25803 var center = imageCanvas.width / 2;
25805 imageContext.translate(center, center);
25807 imageContext.rotate(this.rotate * Math.PI / 180);
25809 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25811 var canvas = document.createElement("canvas");
25813 var context = canvas.getContext("2d");
25815 canvas.width = this.minWidth;
25816 canvas.height = this.minHeight;
25818 switch (this.rotate) {
25821 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25822 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25824 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25825 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25827 var targetWidth = this.minWidth - 2 * x;
25828 var targetHeight = this.minHeight - 2 * y;
25832 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25833 scale = targetWidth / width;
25836 if(x > 0 && y == 0){
25837 scale = targetHeight / height;
25840 if(x > 0 && y > 0){
25841 scale = targetWidth / width;
25843 if(width < height){
25844 scale = targetHeight / height;
25848 context.scale(scale, scale);
25850 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25851 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25853 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25854 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25856 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25861 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25862 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25864 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25865 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25867 var targetWidth = this.minWidth - 2 * x;
25868 var targetHeight = this.minHeight - 2 * y;
25872 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25873 scale = targetWidth / width;
25876 if(x > 0 && y == 0){
25877 scale = targetHeight / height;
25880 if(x > 0 && y > 0){
25881 scale = targetWidth / width;
25883 if(width < height){
25884 scale = targetHeight / height;
25888 context.scale(scale, scale);
25890 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25891 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25893 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25894 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25896 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25898 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25903 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25904 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25906 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25907 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25909 var targetWidth = this.minWidth - 2 * x;
25910 var targetHeight = this.minHeight - 2 * y;
25914 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25915 scale = targetWidth / width;
25918 if(x > 0 && y == 0){
25919 scale = targetHeight / height;
25922 if(x > 0 && y > 0){
25923 scale = targetWidth / width;
25925 if(width < height){
25926 scale = targetHeight / height;
25930 context.scale(scale, scale);
25932 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25933 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25935 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25936 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25938 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25939 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25941 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25946 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25947 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25949 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25950 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25952 var targetWidth = this.minWidth - 2 * x;
25953 var targetHeight = this.minHeight - 2 * y;
25957 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25958 scale = targetWidth / width;
25961 if(x > 0 && y == 0){
25962 scale = targetHeight / height;
25965 if(x > 0 && y > 0){
25966 scale = targetWidth / width;
25968 if(width < height){
25969 scale = targetHeight / height;
25973 context.scale(scale, scale);
25975 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25976 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25978 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25979 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25981 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25983 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25990 this.cropData = canvas.toDataURL(this.cropType);
25992 if(this.fireEvent('crop', this, this.cropData) !== false){
25993 this.process(this.file, this.cropData);
26000 setThumbBoxSize : function()
26004 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26005 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26006 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26008 this.minWidth = width;
26009 this.minHeight = height;
26011 if(this.rotate == 90 || this.rotate == 270){
26012 this.minWidth = height;
26013 this.minHeight = width;
26018 width = Math.ceil(this.minWidth * height / this.minHeight);
26020 if(this.minWidth > this.minHeight){
26022 height = Math.ceil(this.minHeight * width / this.minWidth);
26025 this.thumbEl.setStyle({
26026 width : width + 'px',
26027 height : height + 'px'
26034 setThumbBoxPosition : function()
26036 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26037 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26039 this.thumbEl.setLeft(x);
26040 this.thumbEl.setTop(y);
26044 baseRotateLevel : function()
26046 this.baseRotate = 1;
26049 typeof(this.exif) != 'undefined' &&
26050 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26051 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26053 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26056 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26060 baseScaleLevel : function()
26064 if(this.isDocument){
26066 if(this.baseRotate == 6 || this.baseRotate == 8){
26068 height = this.thumbEl.getHeight();
26069 this.baseScale = height / this.imageEl.OriginWidth;
26071 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26072 width = this.thumbEl.getWidth();
26073 this.baseScale = width / this.imageEl.OriginHeight;
26079 height = this.thumbEl.getHeight();
26080 this.baseScale = height / this.imageEl.OriginHeight;
26082 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26083 width = this.thumbEl.getWidth();
26084 this.baseScale = width / this.imageEl.OriginWidth;
26090 if(this.baseRotate == 6 || this.baseRotate == 8){
26092 width = this.thumbEl.getHeight();
26093 this.baseScale = width / this.imageEl.OriginHeight;
26095 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26096 height = this.thumbEl.getWidth();
26097 this.baseScale = height / this.imageEl.OriginHeight;
26100 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26101 height = this.thumbEl.getWidth();
26102 this.baseScale = height / this.imageEl.OriginHeight;
26104 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26105 width = this.thumbEl.getHeight();
26106 this.baseScale = width / this.imageEl.OriginWidth;
26113 width = this.thumbEl.getWidth();
26114 this.baseScale = width / this.imageEl.OriginWidth;
26116 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26117 height = this.thumbEl.getHeight();
26118 this.baseScale = height / this.imageEl.OriginHeight;
26121 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26123 height = this.thumbEl.getHeight();
26124 this.baseScale = height / this.imageEl.OriginHeight;
26126 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26127 width = this.thumbEl.getWidth();
26128 this.baseScale = width / this.imageEl.OriginWidth;
26136 getScaleLevel : function()
26138 return this.baseScale * Math.pow(1.1, this.scale);
26141 onTouchStart : function(e)
26143 if(!this.canvasLoaded){
26144 this.beforeSelectFile(e);
26148 var touches = e.browserEvent.touches;
26154 if(touches.length == 1){
26155 this.onMouseDown(e);
26159 if(touches.length != 2){
26165 for(var i = 0, finger; finger = touches[i]; i++){
26166 coords.push(finger.pageX, finger.pageY);
26169 var x = Math.pow(coords[0] - coords[2], 2);
26170 var y = Math.pow(coords[1] - coords[3], 2);
26172 this.startDistance = Math.sqrt(x + y);
26174 this.startScale = this.scale;
26176 this.pinching = true;
26177 this.dragable = false;
26181 onTouchMove : function(e)
26183 if(!this.pinching && !this.dragable){
26187 var touches = e.browserEvent.touches;
26194 this.onMouseMove(e);
26200 for(var i = 0, finger; finger = touches[i]; i++){
26201 coords.push(finger.pageX, finger.pageY);
26204 var x = Math.pow(coords[0] - coords[2], 2);
26205 var y = Math.pow(coords[1] - coords[3], 2);
26207 this.endDistance = Math.sqrt(x + y);
26209 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26211 if(!this.zoomable()){
26212 this.scale = this.startScale;
26220 onTouchEnd : function(e)
26222 this.pinching = false;
26223 this.dragable = false;
26227 process : function(file, crop)
26230 this.maskEl.mask(this.loadingText);
26233 this.xhr = new XMLHttpRequest();
26235 file.xhr = this.xhr;
26237 this.xhr.open(this.method, this.url, true);
26240 "Accept": "application/json",
26241 "Cache-Control": "no-cache",
26242 "X-Requested-With": "XMLHttpRequest"
26245 for (var headerName in headers) {
26246 var headerValue = headers[headerName];
26248 this.xhr.setRequestHeader(headerName, headerValue);
26254 this.xhr.onload = function()
26256 _this.xhrOnLoad(_this.xhr);
26259 this.xhr.onerror = function()
26261 _this.xhrOnError(_this.xhr);
26264 var formData = new FormData();
26266 formData.append('returnHTML', 'NO');
26269 formData.append('crop', crop);
26272 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26273 formData.append(this.paramName, file, file.name);
26276 if(typeof(file.filename) != 'undefined'){
26277 formData.append('filename', file.filename);
26280 if(typeof(file.mimetype) != 'undefined'){
26281 formData.append('mimetype', file.mimetype);
26284 if(this.fireEvent('arrange', this, formData) != false){
26285 this.xhr.send(formData);
26289 xhrOnLoad : function(xhr)
26292 this.maskEl.unmask();
26295 if (xhr.readyState !== 4) {
26296 this.fireEvent('exception', this, xhr);
26300 var response = Roo.decode(xhr.responseText);
26302 if(!response.success){
26303 this.fireEvent('exception', this, xhr);
26307 var response = Roo.decode(xhr.responseText);
26309 this.fireEvent('upload', this, response);
26313 xhrOnError : function()
26316 this.maskEl.unmask();
26319 Roo.log('xhr on error');
26321 var response = Roo.decode(xhr.responseText);
26327 prepare : function(file)
26330 this.maskEl.mask(this.loadingText);
26336 if(typeof(file) === 'string'){
26337 this.loadCanvas(file);
26341 if(!file || !this.urlAPI){
26346 this.cropType = file.type;
26350 if(this.fireEvent('prepare', this, this.file) != false){
26352 var reader = new FileReader();
26354 reader.onload = function (e) {
26355 if (e.target.error) {
26356 Roo.log(e.target.error);
26360 var buffer = e.target.result,
26361 dataView = new DataView(buffer),
26363 maxOffset = dataView.byteLength - 4,
26367 if (dataView.getUint16(0) === 0xffd8) {
26368 while (offset < maxOffset) {
26369 markerBytes = dataView.getUint16(offset);
26371 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26372 markerLength = dataView.getUint16(offset + 2) + 2;
26373 if (offset + markerLength > dataView.byteLength) {
26374 Roo.log('Invalid meta data: Invalid segment size.');
26378 if(markerBytes == 0xffe1){
26379 _this.parseExifData(
26386 offset += markerLength;
26396 var url = _this.urlAPI.createObjectURL(_this.file);
26398 _this.loadCanvas(url);
26403 reader.readAsArrayBuffer(this.file);
26409 parseExifData : function(dataView, offset, length)
26411 var tiffOffset = offset + 10,
26415 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26416 // No Exif data, might be XMP data instead
26420 // Check for the ASCII code for "Exif" (0x45786966):
26421 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26422 // No Exif data, might be XMP data instead
26425 if (tiffOffset + 8 > dataView.byteLength) {
26426 Roo.log('Invalid Exif data: Invalid segment size.');
26429 // Check for the two null bytes:
26430 if (dataView.getUint16(offset + 8) !== 0x0000) {
26431 Roo.log('Invalid Exif data: Missing byte alignment offset.');
26434 // Check the byte alignment:
26435 switch (dataView.getUint16(tiffOffset)) {
26437 littleEndian = true;
26440 littleEndian = false;
26443 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26446 // Check for the TIFF tag marker (0x002A):
26447 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26448 Roo.log('Invalid Exif data: Missing TIFF marker.');
26451 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26452 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26454 this.parseExifTags(
26457 tiffOffset + dirOffset,
26462 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26467 if (dirOffset + 6 > dataView.byteLength) {
26468 Roo.log('Invalid Exif data: Invalid directory offset.');
26471 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26472 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26473 if (dirEndOffset + 4 > dataView.byteLength) {
26474 Roo.log('Invalid Exif data: Invalid directory size.');
26477 for (i = 0; i < tagsNumber; i += 1) {
26481 dirOffset + 2 + 12 * i, // tag offset
26485 // Return the offset to the next directory:
26486 return dataView.getUint32(dirEndOffset, littleEndian);
26489 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
26491 var tag = dataView.getUint16(offset, littleEndian);
26493 this.exif[tag] = this.getExifValue(
26497 dataView.getUint16(offset + 2, littleEndian), // tag type
26498 dataView.getUint32(offset + 4, littleEndian), // tag length
26503 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26505 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26514 Roo.log('Invalid Exif data: Invalid tag type.');
26518 tagSize = tagType.size * length;
26519 // Determine if the value is contained in the dataOffset bytes,
26520 // or if the value at the dataOffset is a pointer to the actual data:
26521 dataOffset = tagSize > 4 ?
26522 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26523 if (dataOffset + tagSize > dataView.byteLength) {
26524 Roo.log('Invalid Exif data: Invalid data offset.');
26527 if (length === 1) {
26528 return tagType.getValue(dataView, dataOffset, littleEndian);
26531 for (i = 0; i < length; i += 1) {
26532 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26535 if (tagType.ascii) {
26537 // Concatenate the chars:
26538 for (i = 0; i < values.length; i += 1) {
26540 // Ignore the terminating NULL byte(s):
26541 if (c === '\u0000') {
26553 Roo.apply(Roo.bootstrap.UploadCropbox, {
26555 'Orientation': 0x0112
26559 1: 0, //'top-left',
26561 3: 180, //'bottom-right',
26562 // 4: 'bottom-left',
26564 6: 90, //'right-top',
26565 // 7: 'right-bottom',
26566 8: 270 //'left-bottom'
26570 // byte, 8-bit unsigned int:
26572 getValue: function (dataView, dataOffset) {
26573 return dataView.getUint8(dataOffset);
26577 // ascii, 8-bit byte:
26579 getValue: function (dataView, dataOffset) {
26580 return String.fromCharCode(dataView.getUint8(dataOffset));
26585 // short, 16 bit int:
26587 getValue: function (dataView, dataOffset, littleEndian) {
26588 return dataView.getUint16(dataOffset, littleEndian);
26592 // long, 32 bit int:
26594 getValue: function (dataView, dataOffset, littleEndian) {
26595 return dataView.getUint32(dataOffset, littleEndian);
26599 // rational = two long values, first is numerator, second is denominator:
26601 getValue: function (dataView, dataOffset, littleEndian) {
26602 return dataView.getUint32(dataOffset, littleEndian) /
26603 dataView.getUint32(dataOffset + 4, littleEndian);
26607 // slong, 32 bit signed int:
26609 getValue: function (dataView, dataOffset, littleEndian) {
26610 return dataView.getInt32(dataOffset, littleEndian);
26614 // srational, two slongs, first is numerator, second is denominator:
26616 getValue: function (dataView, dataOffset, littleEndian) {
26617 return dataView.getInt32(dataOffset, littleEndian) /
26618 dataView.getInt32(dataOffset + 4, littleEndian);
26628 cls : 'btn-group roo-upload-cropbox-rotate-left',
26629 action : 'rotate-left',
26633 cls : 'btn btn-default',
26634 html : '<i class="fa fa-undo"></i>'
26640 cls : 'btn-group roo-upload-cropbox-picture',
26641 action : 'picture',
26645 cls : 'btn btn-default',
26646 html : '<i class="fa fa-picture-o"></i>'
26652 cls : 'btn-group roo-upload-cropbox-rotate-right',
26653 action : 'rotate-right',
26657 cls : 'btn btn-default',
26658 html : '<i class="fa fa-repeat"></i>'
26666 cls : 'btn-group roo-upload-cropbox-rotate-left',
26667 action : 'rotate-left',
26671 cls : 'btn btn-default',
26672 html : '<i class="fa fa-undo"></i>'
26678 cls : 'btn-group roo-upload-cropbox-download',
26679 action : 'download',
26683 cls : 'btn btn-default',
26684 html : '<i class="fa fa-download"></i>'
26690 cls : 'btn-group roo-upload-cropbox-crop',
26695 cls : 'btn btn-default',
26696 html : '<i class="fa fa-crop"></i>'
26702 cls : 'btn-group roo-upload-cropbox-trash',
26707 cls : 'btn btn-default',
26708 html : '<i class="fa fa-trash"></i>'
26714 cls : 'btn-group roo-upload-cropbox-rotate-right',
26715 action : 'rotate-right',
26719 cls : 'btn btn-default',
26720 html : '<i class="fa fa-repeat"></i>'
26728 cls : 'btn-group roo-upload-cropbox-rotate-left',
26729 action : 'rotate-left',
26733 cls : 'btn btn-default',
26734 html : '<i class="fa fa-undo"></i>'
26740 cls : 'btn-group roo-upload-cropbox-rotate-right',
26741 action : 'rotate-right',
26745 cls : 'btn btn-default',
26746 html : '<i class="fa fa-repeat"></i>'
26759 * @class Roo.bootstrap.DocumentManager
26760 * @extends Roo.bootstrap.Component
26761 * Bootstrap DocumentManager class
26762 * @cfg {String} paramName default 'imageUpload'
26763 * @cfg {String} method default POST
26764 * @cfg {String} url action url
26765 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26766 * @cfg {Boolean} multiple multiple upload default true
26767 * @cfg {Number} thumbSize default 300
26768 * @cfg {String} fieldLabel
26769 * @cfg {Number} labelWidth default 4
26770 * @cfg {String} labelAlign (left|top) default left
26771 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26774 * Create a new DocumentManager
26775 * @param {Object} config The config object
26778 Roo.bootstrap.DocumentManager = function(config){
26779 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26784 * Fire when initial the DocumentManager
26785 * @param {Roo.bootstrap.DocumentManager} this
26790 * inspect selected file
26791 * @param {Roo.bootstrap.DocumentManager} this
26792 * @param {File} file
26797 * Fire when xhr load exception
26798 * @param {Roo.bootstrap.DocumentManager} this
26799 * @param {XMLHttpRequest} xhr
26801 "exception" : true,
26804 * prepare the form data
26805 * @param {Roo.bootstrap.DocumentManager} this
26806 * @param {Object} formData
26811 * Fire when remove the file
26812 * @param {Roo.bootstrap.DocumentManager} this
26813 * @param {Object} file
26818 * Fire after refresh the file
26819 * @param {Roo.bootstrap.DocumentManager} this
26824 * Fire after click the image
26825 * @param {Roo.bootstrap.DocumentManager} this
26826 * @param {Object} file
26831 * Fire when upload a image and editable set to true
26832 * @param {Roo.bootstrap.DocumentManager} this
26833 * @param {Object} file
26837 * @event beforeselectfile
26838 * Fire before select file
26839 * @param {Roo.bootstrap.DocumentManager} this
26841 "beforeselectfile" : true,
26844 * Fire before process file
26845 * @param {Roo.bootstrap.DocumentManager} this
26846 * @param {Object} file
26853 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26862 paramName : 'imageUpload',
26865 labelAlign : 'left',
26872 getAutoCreate : function()
26874 var managerWidget = {
26876 cls : 'roo-document-manager',
26880 cls : 'roo-document-manager-selector',
26885 cls : 'roo-document-manager-uploader',
26889 cls : 'roo-document-manager-upload-btn',
26890 html : '<i class="fa fa-plus"></i>'
26901 cls : 'column col-md-12',
26906 if(this.fieldLabel.length){
26911 cls : 'column col-md-12',
26912 html : this.fieldLabel
26916 cls : 'column col-md-12',
26921 if(this.labelAlign == 'left'){
26925 cls : 'column col-md-' + this.labelWidth,
26926 html : this.fieldLabel
26930 cls : 'column col-md-' + (12 - this.labelWidth),
26940 cls : 'row clearfix',
26948 initEvents : function()
26950 this.managerEl = this.el.select('.roo-document-manager', true).first();
26951 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26953 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26954 this.selectorEl.hide();
26957 this.selectorEl.attr('multiple', 'multiple');
26960 this.selectorEl.on('change', this.onFileSelected, this);
26962 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26963 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26965 this.uploader.on('click', this.onUploaderClick, this);
26967 this.renderProgressDialog();
26971 window.addEventListener("resize", function() { _this.refresh(); } );
26973 this.fireEvent('initial', this);
26976 renderProgressDialog : function()
26980 this.progressDialog = new Roo.bootstrap.Modal({
26981 cls : 'roo-document-manager-progress-dialog',
26982 allow_close : false,
26992 btnclick : function() {
26993 _this.uploadCancel();
26999 this.progressDialog.render(Roo.get(document.body));
27001 this.progress = new Roo.bootstrap.Progress({
27002 cls : 'roo-document-manager-progress',
27007 this.progress.render(this.progressDialog.getChildContainer());
27009 this.progressBar = new Roo.bootstrap.ProgressBar({
27010 cls : 'roo-document-manager-progress-bar',
27013 aria_valuemax : 12,
27017 this.progressBar.render(this.progress.getChildContainer());
27020 onUploaderClick : function(e)
27022 e.preventDefault();
27024 if(this.fireEvent('beforeselectfile', this) != false){
27025 this.selectorEl.dom.click();
27030 onFileSelected : function(e)
27032 e.preventDefault();
27034 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27038 Roo.each(this.selectorEl.dom.files, function(file){
27039 if(this.fireEvent('inspect', this, file) != false){
27040 this.files.push(file);
27050 this.selectorEl.dom.value = '';
27052 if(!this.files.length){
27056 if(this.boxes > 0 && this.files.length > this.boxes){
27057 this.files = this.files.slice(0, this.boxes);
27060 this.uploader.show();
27062 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27063 this.uploader.hide();
27072 Roo.each(this.files, function(file){
27074 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27075 var f = this.renderPreview(file);
27080 if(file.type.indexOf('image') != -1){
27081 this.delegates.push(
27083 _this.process(file);
27084 }).createDelegate(this)
27092 _this.process(file);
27093 }).createDelegate(this)
27098 this.files = files;
27100 this.delegates = this.delegates.concat(docs);
27102 if(!this.delegates.length){
27107 this.progressBar.aria_valuemax = this.delegates.length;
27114 arrange : function()
27116 if(!this.delegates.length){
27117 this.progressDialog.hide();
27122 var delegate = this.delegates.shift();
27124 this.progressDialog.show();
27126 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27128 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27133 refresh : function()
27135 this.uploader.show();
27137 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27138 this.uploader.hide();
27141 Roo.isTouch ? this.closable(false) : this.closable(true);
27143 this.fireEvent('refresh', this);
27146 onRemove : function(e, el, o)
27148 e.preventDefault();
27150 this.fireEvent('remove', this, o);
27154 remove : function(o)
27158 Roo.each(this.files, function(file){
27159 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27168 this.files = files;
27175 Roo.each(this.files, function(file){
27180 file.target.remove();
27189 onClick : function(e, el, o)
27191 e.preventDefault();
27193 this.fireEvent('click', this, o);
27197 closable : function(closable)
27199 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27201 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27213 xhrOnLoad : function(xhr)
27215 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27219 if (xhr.readyState !== 4) {
27221 this.fireEvent('exception', this, xhr);
27225 var response = Roo.decode(xhr.responseText);
27227 if(!response.success){
27229 this.fireEvent('exception', this, xhr);
27233 var file = this.renderPreview(response.data);
27235 this.files.push(file);
27241 xhrOnError : function(xhr)
27243 Roo.log('xhr on error');
27245 var response = Roo.decode(xhr.responseText);
27252 process : function(file)
27254 if(this.fireEvent('process', this, file) !== false){
27255 if(this.editable && file.type.indexOf('image') != -1){
27256 this.fireEvent('edit', this, file);
27260 this.uploadStart(file, false);
27267 uploadStart : function(file, crop)
27269 this.xhr = new XMLHttpRequest();
27271 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27276 file.xhr = this.xhr;
27278 this.managerEl.createChild({
27280 cls : 'roo-document-manager-loading',
27284 tooltip : file.name,
27285 cls : 'roo-document-manager-thumb',
27286 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27292 this.xhr.open(this.method, this.url, true);
27295 "Accept": "application/json",
27296 "Cache-Control": "no-cache",
27297 "X-Requested-With": "XMLHttpRequest"
27300 for (var headerName in headers) {
27301 var headerValue = headers[headerName];
27303 this.xhr.setRequestHeader(headerName, headerValue);
27309 this.xhr.onload = function()
27311 _this.xhrOnLoad(_this.xhr);
27314 this.xhr.onerror = function()
27316 _this.xhrOnError(_this.xhr);
27319 var formData = new FormData();
27321 formData.append('returnHTML', 'NO');
27324 formData.append('crop', crop);
27327 formData.append(this.paramName, file, file.name);
27329 if(this.fireEvent('prepare', this, formData) != false){
27330 this.xhr.send(formData);
27334 uploadCancel : function()
27341 this.delegates = [];
27343 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27350 renderPreview : function(file)
27352 if(typeof(file.target) != 'undefined' && file.target){
27356 var previewEl = this.managerEl.createChild({
27358 cls : 'roo-document-manager-preview',
27362 tooltip : file.filename,
27363 cls : 'roo-document-manager-thumb',
27364 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27369 html : '<i class="fa fa-times-circle"></i>'
27374 var close = previewEl.select('button.close', true).first();
27376 close.on('click', this.onRemove, this, file);
27378 file.target = previewEl;
27380 var image = previewEl.select('img', true).first();
27384 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27386 image.on('click', this.onClick, this, file);
27392 onPreviewLoad : function(file, image)
27394 if(typeof(file.target) == 'undefined' || !file.target){
27398 var width = image.dom.naturalWidth || image.dom.width;
27399 var height = image.dom.naturalHeight || image.dom.height;
27401 if(width > height){
27402 file.target.addClass('wide');
27406 file.target.addClass('tall');
27411 uploadFromSource : function(file, crop)
27413 this.xhr = new XMLHttpRequest();
27415 this.managerEl.createChild({
27417 cls : 'roo-document-manager-loading',
27421 tooltip : file.name,
27422 cls : 'roo-document-manager-thumb',
27423 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27429 this.xhr.open(this.method, this.url, true);
27432 "Accept": "application/json",
27433 "Cache-Control": "no-cache",
27434 "X-Requested-With": "XMLHttpRequest"
27437 for (var headerName in headers) {
27438 var headerValue = headers[headerName];
27440 this.xhr.setRequestHeader(headerName, headerValue);
27446 this.xhr.onload = function()
27448 _this.xhrOnLoad(_this.xhr);
27451 this.xhr.onerror = function()
27453 _this.xhrOnError(_this.xhr);
27456 var formData = new FormData();
27458 formData.append('returnHTML', 'NO');
27460 formData.append('crop', crop);
27462 if(typeof(file.filename) != 'undefined'){
27463 formData.append('filename', file.filename);
27466 if(typeof(file.mimetype) != 'undefined'){
27467 formData.append('mimetype', file.mimetype);
27470 if(this.fireEvent('prepare', this, formData) != false){
27471 this.xhr.send(formData);
27481 * @class Roo.bootstrap.DocumentViewer
27482 * @extends Roo.bootstrap.Component
27483 * Bootstrap DocumentViewer class
27486 * Create a new DocumentViewer
27487 * @param {Object} config The config object
27490 Roo.bootstrap.DocumentViewer = function(config){
27491 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27496 * Fire after initEvent
27497 * @param {Roo.bootstrap.DocumentViewer} this
27503 * @param {Roo.bootstrap.DocumentViewer} this
27508 * Fire after trash button
27509 * @param {Roo.bootstrap.DocumentViewer} this
27516 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27518 getAutoCreate : function()
27522 cls : 'roo-document-viewer',
27526 cls : 'roo-document-viewer-body',
27530 cls : 'roo-document-viewer-thumb',
27534 cls : 'roo-document-viewer-image'
27542 cls : 'roo-document-viewer-footer',
27545 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27553 cls : 'btn btn-default roo-document-viewer-trash',
27554 html : '<i class="fa fa-trash"></i>'
27567 initEvents : function()
27570 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27571 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27573 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27574 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27576 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27577 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27579 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27580 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27582 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27583 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27585 this.bodyEl.on('click', this.onClick, this);
27587 this.trashBtn.on('click', this.onTrash, this);
27591 initial : function()
27593 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27596 this.fireEvent('initial', this);
27600 onClick : function(e)
27602 e.preventDefault();
27604 this.fireEvent('click', this);
27607 onTrash : function(e)
27609 e.preventDefault();
27611 this.fireEvent('trash', this);
27623 * @class Roo.bootstrap.NavProgressBar
27624 * @extends Roo.bootstrap.Component
27625 * Bootstrap NavProgressBar class
27628 * Create a new nav progress bar
27629 * @param {Object} config The config object
27632 Roo.bootstrap.NavProgressBar = function(config){
27633 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27635 this.bullets = this.bullets || [];
27637 // Roo.bootstrap.NavProgressBar.register(this);
27641 * Fires when the active item changes
27642 * @param {Roo.bootstrap.NavProgressBar} this
27643 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27644 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27651 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27656 getAutoCreate : function()
27658 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27662 cls : 'roo-navigation-bar-group',
27666 cls : 'roo-navigation-top-bar'
27670 cls : 'roo-navigation-bullets-bar',
27674 cls : 'roo-navigation-bar'
27681 cls : 'roo-navigation-bottom-bar'
27691 initEvents: function()
27696 onRender : function(ct, position)
27698 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27700 if(this.bullets.length){
27701 Roo.each(this.bullets, function(b){
27710 addItem : function(cfg)
27712 var item = new Roo.bootstrap.NavProgressItem(cfg);
27714 item.parentId = this.id;
27715 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27718 var top = new Roo.bootstrap.Element({
27720 cls : 'roo-navigation-bar-text'
27723 var bottom = new Roo.bootstrap.Element({
27725 cls : 'roo-navigation-bar-text'
27728 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27729 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27731 var topText = new Roo.bootstrap.Element({
27733 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27736 var bottomText = new Roo.bootstrap.Element({
27738 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27741 topText.onRender(top.el, null);
27742 bottomText.onRender(bottom.el, null);
27745 item.bottomEl = bottom;
27748 this.barItems.push(item);
27753 getActive : function()
27755 var active = false;
27757 Roo.each(this.barItems, function(v){
27759 if (!v.isActive()) {
27771 setActiveItem : function(item)
27775 Roo.each(this.barItems, function(v){
27776 if (v.rid == item.rid) {
27780 if (v.isActive()) {
27781 v.setActive(false);
27786 item.setActive(true);
27788 this.fireEvent('changed', this, item, prev);
27791 getBarItem: function(rid)
27795 Roo.each(this.barItems, function(e) {
27796 if (e.rid != rid) {
27807 indexOfItem : function(item)
27811 Roo.each(this.barItems, function(v, i){
27813 if (v.rid != item.rid) {
27824 setActiveNext : function()
27826 var i = this.indexOfItem(this.getActive());
27828 if (i > this.barItems.length) {
27832 this.setActiveItem(this.barItems[i+1]);
27835 setActivePrev : function()
27837 var i = this.indexOfItem(this.getActive());
27843 this.setActiveItem(this.barItems[i-1]);
27846 format : function()
27848 if(!this.barItems.length){
27852 var width = 100 / this.barItems.length;
27854 Roo.each(this.barItems, function(i){
27855 i.el.setStyle('width', width + '%');
27856 i.topEl.el.setStyle('width', width + '%');
27857 i.bottomEl.el.setStyle('width', width + '%');
27866 * Nav Progress Item
27871 * @class Roo.bootstrap.NavProgressItem
27872 * @extends Roo.bootstrap.Component
27873 * Bootstrap NavProgressItem class
27874 * @cfg {String} rid the reference id
27875 * @cfg {Boolean} active (true|false) Is item active default false
27876 * @cfg {Boolean} disabled (true|false) Is item active default false
27877 * @cfg {String} html
27878 * @cfg {String} position (top|bottom) text position default bottom
27879 * @cfg {String} icon show icon instead of number
27882 * Create a new NavProgressItem
27883 * @param {Object} config The config object
27885 Roo.bootstrap.NavProgressItem = function(config){
27886 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27891 * The raw click event for the entire grid.
27892 * @param {Roo.bootstrap.NavProgressItem} this
27893 * @param {Roo.EventObject} e
27900 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27906 position : 'bottom',
27909 getAutoCreate : function()
27911 var iconCls = 'roo-navigation-bar-item-icon';
27913 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27917 cls: 'roo-navigation-bar-item',
27927 cfg.cls += ' active';
27930 cfg.cls += ' disabled';
27936 disable : function()
27938 this.setDisabled(true);
27941 enable : function()
27943 this.setDisabled(false);
27946 initEvents: function()
27948 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27950 this.iconEl.on('click', this.onClick, this);
27953 onClick : function(e)
27955 e.preventDefault();
27961 if(this.fireEvent('click', this, e) === false){
27965 this.parent().setActiveItem(this);
27968 isActive: function ()
27970 return this.active;
27973 setActive : function(state)
27975 if(this.active == state){
27979 this.active = state;
27982 this.el.addClass('active');
27986 this.el.removeClass('active');
27991 setDisabled : function(state)
27993 if(this.disabled == state){
27997 this.disabled = state;
28000 this.el.addClass('disabled');
28004 this.el.removeClass('disabled');
28007 tooltipEl : function()
28009 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28022 * @class Roo.bootstrap.FieldLabel
28023 * @extends Roo.bootstrap.Component
28024 * Bootstrap FieldLabel class
28025 * @cfg {String} html contents of the element
28026 * @cfg {String} tag tag of the element default label
28027 * @cfg {String} cls class of the element
28028 * @cfg {String} target label target
28029 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28030 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28031 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28032 * @cfg {String} iconTooltip default "This field is required"
28035 * Create a new FieldLabel
28036 * @param {Object} config The config object
28039 Roo.bootstrap.FieldLabel = function(config){
28040 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28045 * Fires after the field has been marked as invalid.
28046 * @param {Roo.form.FieldLabel} this
28047 * @param {String} msg The validation message
28052 * Fires after the field has been validated with no errors.
28053 * @param {Roo.form.FieldLabel} this
28059 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28066 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28067 validClass : 'text-success fa fa-lg fa-check',
28068 iconTooltip : 'This field is required',
28070 getAutoCreate : function(){
28074 cls : 'roo-bootstrap-field-label ' + this.cls,
28080 tooltip : this.iconTooltip
28092 initEvents: function()
28094 Roo.bootstrap.Element.superclass.initEvents.call(this);
28096 this.iconEl = this.el.select('i', true).first();
28098 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28100 Roo.bootstrap.FieldLabel.register(this);
28104 * Mark this field as valid
28106 markValid : function()
28108 this.iconEl.show();
28110 this.iconEl.removeClass(this.invalidClass);
28112 this.iconEl.addClass(this.validClass);
28114 this.fireEvent('valid', this);
28118 * Mark this field as invalid
28119 * @param {String} msg The validation message
28121 markInvalid : function(msg)
28123 this.iconEl.show();
28125 this.iconEl.removeClass(this.validClass);
28127 this.iconEl.addClass(this.invalidClass);
28129 this.fireEvent('invalid', this, msg);
28135 Roo.apply(Roo.bootstrap.FieldLabel, {
28140 * register a FieldLabel Group
28141 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28143 register : function(label)
28145 if(this.groups.hasOwnProperty(label.target)){
28149 this.groups[label.target] = label;
28153 * fetch a FieldLabel Group based on the target
28154 * @param {string} target
28155 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28157 get: function(target) {
28158 if (typeof(this.groups[target]) == 'undefined') {
28162 return this.groups[target] ;
28171 * page DateSplitField.
28177 * @class Roo.bootstrap.DateSplitField
28178 * @extends Roo.bootstrap.Component
28179 * Bootstrap DateSplitField class
28180 * @cfg {string} fieldLabel - the label associated
28181 * @cfg {Number} labelWidth set the width of label (0-12)
28182 * @cfg {String} labelAlign (top|left)
28183 * @cfg {Boolean} dayAllowBlank (true|false) default false
28184 * @cfg {Boolean} monthAllowBlank (true|false) default false
28185 * @cfg {Boolean} yearAllowBlank (true|false) default false
28186 * @cfg {string} dayPlaceholder
28187 * @cfg {string} monthPlaceholder
28188 * @cfg {string} yearPlaceholder
28189 * @cfg {string} dayFormat default 'd'
28190 * @cfg {string} monthFormat default 'm'
28191 * @cfg {string} yearFormat default 'Y'
28195 * Create a new DateSplitField
28196 * @param {Object} config The config object
28199 Roo.bootstrap.DateSplitField = function(config){
28200 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28206 * getting the data of years
28207 * @param {Roo.bootstrap.DateSplitField} this
28208 * @param {Object} years
28213 * getting the data of days
28214 * @param {Roo.bootstrap.DateSplitField} this
28215 * @param {Object} days
28220 * Fires after the field has been marked as invalid.
28221 * @param {Roo.form.Field} this
28222 * @param {String} msg The validation message
28227 * Fires after the field has been validated with no errors.
28228 * @param {Roo.form.Field} this
28234 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28237 labelAlign : 'top',
28239 dayAllowBlank : false,
28240 monthAllowBlank : false,
28241 yearAllowBlank : false,
28242 dayPlaceholder : '',
28243 monthPlaceholder : '',
28244 yearPlaceholder : '',
28248 isFormField : true,
28250 getAutoCreate : function()
28254 cls : 'row roo-date-split-field-group',
28259 cls : 'form-hidden-field roo-date-split-field-group-value',
28265 if(this.fieldLabel){
28268 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28272 html : this.fieldLabel
28278 Roo.each(['day', 'month', 'year'], function(t){
28281 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28288 inputEl: function ()
28290 return this.el.select('.roo-date-split-field-group-value', true).first();
28293 onRender : function(ct, position)
28297 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28299 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28301 this.dayField = new Roo.bootstrap.ComboBox({
28302 allowBlank : this.dayAllowBlank,
28303 alwaysQuery : true,
28304 displayField : 'value',
28307 forceSelection : true,
28309 placeholder : this.dayPlaceholder,
28310 selectOnFocus : true,
28311 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28312 triggerAction : 'all',
28314 valueField : 'value',
28315 store : new Roo.data.SimpleStore({
28316 data : (function() {
28318 _this.fireEvent('days', _this, days);
28321 fields : [ 'value' ]
28324 select : function (_self, record, index)
28326 _this.setValue(_this.getValue());
28331 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28333 this.monthField = new Roo.bootstrap.MonthField({
28334 after : '<i class=\"fa fa-calendar\"></i>',
28335 allowBlank : this.monthAllowBlank,
28336 placeholder : this.monthPlaceholder,
28339 render : function (_self)
28341 this.el.select('span.input-group-addon', true).first().on('click', function(e){
28342 e.preventDefault();
28346 select : function (_self, oldvalue, newvalue)
28348 _this.setValue(_this.getValue());
28353 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28355 this.yearField = new Roo.bootstrap.ComboBox({
28356 allowBlank : this.yearAllowBlank,
28357 alwaysQuery : true,
28358 displayField : 'value',
28361 forceSelection : true,
28363 placeholder : this.yearPlaceholder,
28364 selectOnFocus : true,
28365 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28366 triggerAction : 'all',
28368 valueField : 'value',
28369 store : new Roo.data.SimpleStore({
28370 data : (function() {
28372 _this.fireEvent('years', _this, years);
28375 fields : [ 'value' ]
28378 select : function (_self, record, index)
28380 _this.setValue(_this.getValue());
28385 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28388 setValue : function(v, format)
28390 this.inputEl.dom.value = v;
28392 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28394 var d = Date.parseDate(v, f);
28401 this.setDay(d.format(this.dayFormat));
28402 this.setMonth(d.format(this.monthFormat));
28403 this.setYear(d.format(this.yearFormat));
28410 setDay : function(v)
28412 this.dayField.setValue(v);
28413 this.inputEl.dom.value = this.getValue();
28418 setMonth : function(v)
28420 this.monthField.setValue(v, true);
28421 this.inputEl.dom.value = this.getValue();
28426 setYear : function(v)
28428 this.yearField.setValue(v);
28429 this.inputEl.dom.value = this.getValue();
28434 getDay : function()
28436 return this.dayField.getValue();
28439 getMonth : function()
28441 return this.monthField.getValue();
28444 getYear : function()
28446 return this.yearField.getValue();
28449 getValue : function()
28451 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28453 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28463 this.inputEl.dom.value = '';
28468 validate : function()
28470 var d = this.dayField.validate();
28471 var m = this.monthField.validate();
28472 var y = this.yearField.validate();
28477 (!this.dayAllowBlank && !d) ||
28478 (!this.monthAllowBlank && !m) ||
28479 (!this.yearAllowBlank && !y)
28484 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28493 this.markInvalid();
28498 markValid : function()
28501 var label = this.el.select('label', true).first();
28502 var icon = this.el.select('i.fa-star', true).first();
28508 this.fireEvent('valid', this);
28512 * Mark this field as invalid
28513 * @param {String} msg The validation message
28515 markInvalid : function(msg)
28518 var label = this.el.select('label', true).first();
28519 var icon = this.el.select('i.fa-star', true).first();
28521 if(label && !icon){
28522 this.el.select('.roo-date-split-field-label', true).createChild({
28524 cls : 'text-danger fa fa-lg fa-star',
28525 tooltip : 'This field is required',
28526 style : 'margin-right:5px;'
28530 this.fireEvent('invalid', this, msg);
28533 clearInvalid : function()
28535 var label = this.el.select('label', true).first();
28536 var icon = this.el.select('i.fa-star', true).first();
28542 this.fireEvent('valid', this);
28545 getName: function()
28555 * http://masonry.desandro.com
28557 * The idea is to render all the bricks based on vertical width...
28559 * The original code extends 'outlayer' - we might need to use that....
28565 * @class Roo.bootstrap.LayoutMasonry
28566 * @extends Roo.bootstrap.Component
28567 * Bootstrap Layout Masonry class
28570 * Create a new Element
28571 * @param {Object} config The config object
28574 Roo.bootstrap.LayoutMasonry = function(config){
28575 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28581 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
28584 * @cfg {Boolean} isLayoutInstant = no animation?
28586 isLayoutInstant : false, // needed?
28589 * @cfg {Number} boxWidth width of the columns
28594 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
28599 * @cfg {Number} padWidth padding below box..
28604 * @cfg {Number} gutter gutter width..
28609 * @cfg {Number} maxCols maximum number of columns
28615 * @cfg {Boolean} isAutoInitial defalut true
28617 isAutoInitial : true,
28622 * @cfg {Boolean} isHorizontal defalut false
28624 isHorizontal : false,
28626 currentSize : null,
28632 bricks: null, //CompositeElement
28636 _isLayoutInited : false,
28638 // isAlternative : false, // only use for vertical layout...
28641 * @cfg {Number} alternativePadWidth padding below box..
28643 alternativePadWidth : 50,
28645 getAutoCreate : function(){
28649 cls: 'blog-masonary-wrapper ' + this.cls,
28651 cls : 'mas-boxes masonary'
28658 getChildContainer: function( )
28660 if (this.boxesEl) {
28661 return this.boxesEl;
28664 this.boxesEl = this.el.select('.mas-boxes').first();
28666 return this.boxesEl;
28670 initEvents : function()
28674 if(this.isAutoInitial){
28675 Roo.log('hook children rendered');
28676 this.on('childrenrendered', function() {
28677 Roo.log('children rendered');
28683 initial : function()
28685 this.currentSize = this.el.getBox(true);
28687 Roo.EventManager.onWindowResize(this.resize, this);
28689 if(!this.isAutoInitial){
28697 //this.layout.defer(500,this);
28701 resize : function()
28705 var cs = this.el.getBox(true);
28707 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28708 Roo.log("no change in with or X");
28712 this.currentSize = cs;
28718 layout : function()
28720 this._resetLayout();
28722 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28724 this.layoutItems( isInstant );
28726 this._isLayoutInited = true;
28730 _resetLayout : function()
28732 if(this.isHorizontal){
28733 this.horizontalMeasureColumns();
28737 this.verticalMeasureColumns();
28741 verticalMeasureColumns : function()
28743 this.getContainerWidth();
28745 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28746 // this.colWidth = Math.floor(this.containerWidth * 0.8);
28750 var boxWidth = this.boxWidth + this.padWidth;
28752 if(this.containerWidth < this.boxWidth){
28753 boxWidth = this.containerWidth
28756 var containerWidth = this.containerWidth;
28758 var cols = Math.floor(containerWidth / boxWidth);
28760 this.cols = Math.max( cols, 1 );
28762 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
28764 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28766 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28768 this.colWidth = boxWidth + avail - this.padWidth;
28770 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28771 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
28774 horizontalMeasureColumns : function()
28776 this.getContainerWidth();
28778 var boxWidth = this.boxWidth;
28780 if(this.containerWidth < boxWidth){
28781 boxWidth = this.containerWidth;
28784 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28786 this.el.setHeight(boxWidth);
28790 getContainerWidth : function()
28792 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
28795 layoutItems : function( isInstant )
28797 var items = Roo.apply([], this.bricks);
28799 if(this.isHorizontal){
28800 this._horizontalLayoutItems( items , isInstant );
28804 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28805 // this._verticalAlternativeLayoutItems( items , isInstant );
28809 this._verticalLayoutItems( items , isInstant );
28813 _verticalLayoutItems : function ( items , isInstant)
28815 if ( !items || !items.length ) {
28820 ['xs', 'xs', 'xs', 'tall'],
28821 ['xs', 'xs', 'tall'],
28822 ['xs', 'xs', 'sm'],
28823 ['xs', 'xs', 'xs'],
28829 ['sm', 'xs', 'xs'],
28833 ['tall', 'xs', 'xs', 'xs'],
28834 ['tall', 'xs', 'xs'],
28846 Roo.each(items, function(item, k){
28848 switch (item.size) {
28849 // these layouts take up a full box,
28860 boxes.push([item]);
28883 var filterPattern = function(box, length)
28891 var pattern = box.slice(0, length);
28895 Roo.each(pattern, function(i){
28896 format.push(i.size);
28899 Roo.each(standard, function(s){
28901 if(String(s) != String(format)){
28910 if(!match && length == 1){
28915 filterPattern(box, length - 1);
28919 queue.push(pattern);
28921 box = box.slice(length, box.length);
28923 filterPattern(box, 4);
28929 Roo.each(boxes, function(box, k){
28935 if(box.length == 1){
28940 filterPattern(box, 4);
28944 this._processVerticalLayoutQueue( queue, isInstant );
28948 // _verticalAlternativeLayoutItems : function( items , isInstant )
28950 // if ( !items || !items.length ) {
28954 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
28958 _horizontalLayoutItems : function ( items , isInstant)
28960 if ( !items || !items.length || items.length < 3) {
28966 var eItems = items.slice(0, 3);
28968 items = items.slice(3, items.length);
28971 ['xs', 'xs', 'xs', 'wide'],
28972 ['xs', 'xs', 'wide'],
28973 ['xs', 'xs', 'sm'],
28974 ['xs', 'xs', 'xs'],
28980 ['sm', 'xs', 'xs'],
28984 ['wide', 'xs', 'xs', 'xs'],
28985 ['wide', 'xs', 'xs'],
28998 Roo.each(items, function(item, k){
29000 switch (item.size) {
29011 boxes.push([item]);
29035 var filterPattern = function(box, length)
29043 var pattern = box.slice(0, length);
29047 Roo.each(pattern, function(i){
29048 format.push(i.size);
29051 Roo.each(standard, function(s){
29053 if(String(s) != String(format)){
29062 if(!match && length == 1){
29067 filterPattern(box, length - 1);
29071 queue.push(pattern);
29073 box = box.slice(length, box.length);
29075 filterPattern(box, 4);
29081 Roo.each(boxes, function(box, k){
29087 if(box.length == 1){
29092 filterPattern(box, 4);
29099 var pos = this.el.getBox(true);
29103 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29105 var hit_end = false;
29107 Roo.each(queue, function(box){
29111 Roo.each(box, function(b){
29113 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29123 Roo.each(box, function(b){
29125 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29128 mx = Math.max(mx, b.x);
29132 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29136 Roo.each(box, function(b){
29138 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29152 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29155 /** Sets position of item in DOM
29156 * @param {Element} item
29157 * @param {Number} x - horizontal position
29158 * @param {Number} y - vertical position
29159 * @param {Boolean} isInstant - disables transitions
29161 _processVerticalLayoutQueue : function( queue, isInstant )
29163 var pos = this.el.getBox(true);
29168 for (var i = 0; i < this.cols; i++){
29172 Roo.each(queue, function(box, k){
29174 var col = k % this.cols;
29176 Roo.each(box, function(b,kk){
29178 b.el.position('absolute');
29180 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29181 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29183 if(b.size == 'md-left' || b.size == 'md-right'){
29184 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29185 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29188 b.el.setWidth(width);
29189 b.el.setHeight(height);
29191 b.el.select('iframe',true).setSize(width,height);
29195 for (var i = 0; i < this.cols; i++){
29197 if(maxY[i] < maxY[col]){
29202 col = Math.min(col, i);
29206 x = pos.x + col * (this.colWidth + this.padWidth);
29210 var positions = [];
29212 switch (box.length){
29214 positions = this.getVerticalOneBoxColPositions(x, y, box);
29217 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29220 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29223 positions = this.getVerticalFourBoxColPositions(x, y, box);
29229 Roo.each(box, function(b,kk){
29231 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29233 var sz = b.el.getSize();
29235 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29243 for (var i = 0; i < this.cols; i++){
29244 mY = Math.max(mY, maxY[i]);
29247 this.el.setHeight(mY - pos.y);
29251 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29253 // var pos = this.el.getBox(true);
29256 // var maxX = pos.right;
29258 // var maxHeight = 0;
29260 // Roo.each(items, function(item, k){
29264 // item.el.position('absolute');
29266 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29268 // item.el.setWidth(width);
29270 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29272 // item.el.setHeight(height);
29275 // item.el.setXY([x, y], isInstant ? false : true);
29277 // item.el.setXY([maxX - width, y], isInstant ? false : true);
29280 // y = y + height + this.alternativePadWidth;
29282 // maxHeight = maxHeight + height + this.alternativePadWidth;
29286 // this.el.setHeight(maxHeight);
29290 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29292 var pos = this.el.getBox(true);
29297 var maxX = pos.right;
29299 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29301 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29303 Roo.each(queue, function(box, k){
29305 Roo.each(box, function(b, kk){
29307 b.el.position('absolute');
29309 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29310 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29312 if(b.size == 'md-left' || b.size == 'md-right'){
29313 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29314 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29317 b.el.setWidth(width);
29318 b.el.setHeight(height);
29326 var positions = [];
29328 switch (box.length){
29330 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29333 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29336 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29339 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29345 Roo.each(box, function(b,kk){
29347 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29349 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29357 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29359 Roo.each(eItems, function(b,k){
29361 b.size = (k == 0) ? 'sm' : 'xs';
29362 b.x = (k == 0) ? 2 : 1;
29363 b.y = (k == 0) ? 2 : 1;
29365 b.el.position('absolute');
29367 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29369 b.el.setWidth(width);
29371 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29373 b.el.setHeight(height);
29377 var positions = [];
29380 x : maxX - this.unitWidth * 2 - this.gutter,
29385 x : maxX - this.unitWidth,
29386 y : minY + (this.unitWidth + this.gutter) * 2
29390 x : maxX - this.unitWidth * 3 - this.gutter * 2,
29394 Roo.each(eItems, function(b,k){
29396 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29402 getVerticalOneBoxColPositions : function(x, y, box)
29406 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29408 if(box[0].size == 'md-left'){
29412 if(box[0].size == 'md-right'){
29417 x : x + (this.unitWidth + this.gutter) * rand,
29424 getVerticalTwoBoxColPositions : function(x, y, box)
29428 if(box[0].size == 'xs'){
29432 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29436 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29450 x : x + (this.unitWidth + this.gutter) * 2,
29451 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29458 getVerticalThreeBoxColPositions : function(x, y, box)
29462 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29470 x : x + (this.unitWidth + this.gutter) * 1,
29475 x : x + (this.unitWidth + this.gutter) * 2,
29483 if(box[0].size == 'xs' && box[1].size == 'xs'){
29492 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29496 x : x + (this.unitWidth + this.gutter) * 1,
29510 x : x + (this.unitWidth + this.gutter) * 2,
29515 x : x + (this.unitWidth + this.gutter) * 2,
29516 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29523 getVerticalFourBoxColPositions : function(x, y, box)
29527 if(box[0].size == 'xs'){
29536 y : y + (this.unitHeight + this.gutter) * 1
29541 y : y + (this.unitHeight + this.gutter) * 2
29545 x : x + (this.unitWidth + this.gutter) * 1,
29559 x : x + (this.unitWidth + this.gutter) * 2,
29564 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29565 y : y + (this.unitHeight + this.gutter) * 1
29569 x : x + (this.unitWidth + this.gutter) * 2,
29570 y : y + (this.unitWidth + this.gutter) * 2
29577 getHorizontalOneBoxColPositions : function(maxX, minY, box)
29581 if(box[0].size == 'md-left'){
29583 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29590 if(box[0].size == 'md-right'){
29592 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29593 y : minY + (this.unitWidth + this.gutter) * 1
29599 var rand = Math.floor(Math.random() * (4 - box[0].y));
29602 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29603 y : minY + (this.unitWidth + this.gutter) * rand
29610 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29614 if(box[0].size == 'xs'){
29617 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29622 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29623 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29631 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29636 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29637 y : minY + (this.unitWidth + this.gutter) * 2
29644 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29648 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29651 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29656 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29657 y : minY + (this.unitWidth + this.gutter) * 1
29661 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29662 y : minY + (this.unitWidth + this.gutter) * 2
29669 if(box[0].size == 'xs' && box[1].size == 'xs'){
29672 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29677 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29682 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29683 y : minY + (this.unitWidth + this.gutter) * 1
29691 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29696 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29697 y : minY + (this.unitWidth + this.gutter) * 2
29701 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29702 y : minY + (this.unitWidth + this.gutter) * 2
29709 getHorizontalFourBoxColPositions : function(maxX, minY, box)
29713 if(box[0].size == 'xs'){
29716 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29721 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29726 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),
29731 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29732 y : minY + (this.unitWidth + this.gutter) * 1
29740 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29745 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29746 y : minY + (this.unitWidth + this.gutter) * 2
29750 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29751 y : minY + (this.unitWidth + this.gutter) * 2
29755 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),
29756 y : minY + (this.unitWidth + this.gutter) * 2
29770 * http://masonry.desandro.com
29772 * The idea is to render all the bricks based on vertical width...
29774 * The original code extends 'outlayer' - we might need to use that....
29780 * @class Roo.bootstrap.LayoutMasonryAuto
29781 * @extends Roo.bootstrap.Component
29782 * Bootstrap Layout Masonry class
29785 * Create a new Element
29786 * @param {Object} config The config object
29789 Roo.bootstrap.LayoutMasonryAuto = function(config){
29790 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
29793 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
29796 * @cfg {Boolean} isFitWidth - resize the width..
29798 isFitWidth : false, // options..
29800 * @cfg {Boolean} isOriginLeft = left align?
29802 isOriginLeft : true,
29804 * @cfg {Boolean} isOriginTop = top align?
29806 isOriginTop : false,
29808 * @cfg {Boolean} isLayoutInstant = no animation?
29810 isLayoutInstant : false, // needed?
29812 * @cfg {Boolean} isResizingContainer = not sure if this is used..
29814 isResizingContainer : true,
29816 * @cfg {Number} columnWidth width of the columns
29822 * @cfg {Number} maxCols maximum number of columns
29827 * @cfg {Number} padHeight padding below box..
29833 * @cfg {Boolean} isAutoInitial defalut true
29836 isAutoInitial : true,
29842 initialColumnWidth : 0,
29843 currentSize : null,
29845 colYs : null, // array.
29852 bricks: null, //CompositeElement
29853 cols : 0, // array?
29854 // element : null, // wrapped now this.el
29855 _isLayoutInited : null,
29858 getAutoCreate : function(){
29862 cls: 'blog-masonary-wrapper ' + this.cls,
29864 cls : 'mas-boxes masonary'
29871 getChildContainer: function( )
29873 if (this.boxesEl) {
29874 return this.boxesEl;
29877 this.boxesEl = this.el.select('.mas-boxes').first();
29879 return this.boxesEl;
29883 initEvents : function()
29887 if(this.isAutoInitial){
29888 Roo.log('hook children rendered');
29889 this.on('childrenrendered', function() {
29890 Roo.log('children rendered');
29897 initial : function()
29899 this.reloadItems();
29901 this.currentSize = this.el.getBox(true);
29903 /// was window resize... - let's see if this works..
29904 Roo.EventManager.onWindowResize(this.resize, this);
29906 if(!this.isAutoInitial){
29911 this.layout.defer(500,this);
29914 reloadItems: function()
29916 this.bricks = this.el.select('.masonry-brick', true);
29918 this.bricks.each(function(b) {
29919 //Roo.log(b.getSize());
29920 if (!b.attr('originalwidth')) {
29921 b.attr('originalwidth', b.getSize().width);
29926 Roo.log(this.bricks.elements.length);
29929 resize : function()
29932 var cs = this.el.getBox(true);
29934 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29935 Roo.log("no change in with or X");
29938 this.currentSize = cs;
29942 layout : function()
29945 this._resetLayout();
29946 //this._manageStamps();
29948 // don't animate first layout
29949 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29950 this.layoutItems( isInstant );
29952 // flag for initalized
29953 this._isLayoutInited = true;
29956 layoutItems : function( isInstant )
29958 //var items = this._getItemsForLayout( this.items );
29959 // original code supports filtering layout items.. we just ignore it..
29961 this._layoutItems( this.bricks , isInstant );
29963 this._postLayout();
29965 _layoutItems : function ( items , isInstant)
29967 //this.fireEvent( 'layout', this, items );
29970 if ( !items || !items.elements.length ) {
29971 // no items, emit event with empty array
29976 items.each(function(item) {
29977 Roo.log("layout item");
29979 // get x/y object from method
29980 var position = this._getItemLayoutPosition( item );
29982 position.item = item;
29983 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
29984 queue.push( position );
29987 this._processLayoutQueue( queue );
29989 /** Sets position of item in DOM
29990 * @param {Element} item
29991 * @param {Number} x - horizontal position
29992 * @param {Number} y - vertical position
29993 * @param {Boolean} isInstant - disables transitions
29995 _processLayoutQueue : function( queue )
29997 for ( var i=0, len = queue.length; i < len; i++ ) {
29998 var obj = queue[i];
29999 obj.item.position('absolute');
30000 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30006 * Any logic you want to do after each layout,
30007 * i.e. size the container
30009 _postLayout : function()
30011 this.resizeContainer();
30014 resizeContainer : function()
30016 if ( !this.isResizingContainer ) {
30019 var size = this._getContainerSize();
30021 this.el.setSize(size.width,size.height);
30022 this.boxesEl.setSize(size.width,size.height);
30028 _resetLayout : function()
30030 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30031 this.colWidth = this.el.getWidth();
30032 //this.gutter = this.el.getWidth();
30034 this.measureColumns();
30040 this.colYs.push( 0 );
30046 measureColumns : function()
30048 this.getContainerWidth();
30049 // if columnWidth is 0, default to outerWidth of first item
30050 if ( !this.columnWidth ) {
30051 var firstItem = this.bricks.first();
30052 Roo.log(firstItem);
30053 this.columnWidth = this.containerWidth;
30054 if (firstItem && firstItem.attr('originalwidth') ) {
30055 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30057 // columnWidth fall back to item of first element
30058 Roo.log("set column width?");
30059 this.initialColumnWidth = this.columnWidth ;
30061 // if first elem has no width, default to size of container
30066 if (this.initialColumnWidth) {
30067 this.columnWidth = this.initialColumnWidth;
30072 // column width is fixed at the top - however if container width get's smaller we should
30075 // this bit calcs how man columns..
30077 var columnWidth = this.columnWidth += this.gutter;
30079 // calculate columns
30080 var containerWidth = this.containerWidth + this.gutter;
30082 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30083 // fix rounding errors, typically with gutters
30084 var excess = columnWidth - containerWidth % columnWidth;
30087 // if overshoot is less than a pixel, round up, otherwise floor it
30088 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30089 cols = Math[ mathMethod ]( cols );
30090 this.cols = Math.max( cols, 1 );
30091 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30093 // padding positioning..
30094 var totalColWidth = this.cols * this.columnWidth;
30095 var padavail = this.containerWidth - totalColWidth;
30096 // so for 2 columns - we need 3 'pads'
30098 var padNeeded = (1+this.cols) * this.padWidth;
30100 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30102 this.columnWidth += padExtra
30103 //this.padWidth = Math.floor(padavail / ( this.cols));
30105 // adjust colum width so that padding is fixed??
30107 // we have 3 columns ... total = width * 3
30108 // we have X left over... that should be used by
30110 //if (this.expandC) {
30118 getContainerWidth : function()
30120 /* // container is parent if fit width
30121 var container = this.isFitWidth ? this.element.parentNode : this.element;
30122 // check that this.size and size are there
30123 // IE8 triggers resize on body size change, so they might not be
30125 var size = getSize( container ); //FIXME
30126 this.containerWidth = size && size.innerWidth; //FIXME
30129 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30133 _getItemLayoutPosition : function( item ) // what is item?
30135 // we resize the item to our columnWidth..
30137 item.setWidth(this.columnWidth);
30138 item.autoBoxAdjust = false;
30140 var sz = item.getSize();
30142 // how many columns does this brick span
30143 var remainder = this.containerWidth % this.columnWidth;
30145 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30146 // round if off by 1 pixel, otherwise use ceil
30147 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30148 colSpan = Math.min( colSpan, this.cols );
30150 // normally this should be '1' as we dont' currently allow multi width columns..
30152 var colGroup = this._getColGroup( colSpan );
30153 // get the minimum Y value from the columns
30154 var minimumY = Math.min.apply( Math, colGroup );
30155 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30157 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30159 // position the brick
30161 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30162 y: this.currentSize.y + minimumY + this.padHeight
30166 // apply setHeight to necessary columns
30167 var setHeight = minimumY + sz.height + this.padHeight;
30168 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30170 var setSpan = this.cols + 1 - colGroup.length;
30171 for ( var i = 0; i < setSpan; i++ ) {
30172 this.colYs[ shortColIndex + i ] = setHeight ;
30179 * @param {Number} colSpan - number of columns the element spans
30180 * @returns {Array} colGroup
30182 _getColGroup : function( colSpan )
30184 if ( colSpan < 2 ) {
30185 // if brick spans only one column, use all the column Ys
30190 // how many different places could this brick fit horizontally
30191 var groupCount = this.cols + 1 - colSpan;
30192 // for each group potential horizontal position
30193 for ( var i = 0; i < groupCount; i++ ) {
30194 // make an array of colY values for that one group
30195 var groupColYs = this.colYs.slice( i, i + colSpan );
30196 // and get the max value of the array
30197 colGroup[i] = Math.max.apply( Math, groupColYs );
30202 _manageStamp : function( stamp )
30204 var stampSize = stamp.getSize();
30205 var offset = stamp.getBox();
30206 // get the columns that this stamp affects
30207 var firstX = this.isOriginLeft ? offset.x : offset.right;
30208 var lastX = firstX + stampSize.width;
30209 var firstCol = Math.floor( firstX / this.columnWidth );
30210 firstCol = Math.max( 0, firstCol );
30212 var lastCol = Math.floor( lastX / this.columnWidth );
30213 // lastCol should not go over if multiple of columnWidth #425
30214 lastCol -= lastX % this.columnWidth ? 0 : 1;
30215 lastCol = Math.min( this.cols - 1, lastCol );
30217 // set colYs to bottom of the stamp
30218 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30221 for ( var i = firstCol; i <= lastCol; i++ ) {
30222 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30227 _getContainerSize : function()
30229 this.maxY = Math.max.apply( Math, this.colYs );
30234 if ( this.isFitWidth ) {
30235 size.width = this._getContainerFitWidth();
30241 _getContainerFitWidth : function()
30243 var unusedCols = 0;
30244 // count unused columns
30247 if ( this.colYs[i] !== 0 ) {
30252 // fit container to columns that have been used
30253 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30256 needsResizeLayout : function()
30258 var previousWidth = this.containerWidth;
30259 this.getContainerWidth();
30260 return previousWidth !== this.containerWidth;
30275 * @class Roo.bootstrap.MasonryBrick
30276 * @extends Roo.bootstrap.Component
30277 * Bootstrap MasonryBrick class
30280 * Create a new MasonryBrick
30281 * @param {Object} config The config object
30284 Roo.bootstrap.MasonryBrick = function(config){
30285 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30291 * When a MasonryBrick is clcik
30292 * @param {Roo.bootstrap.MasonryBrick} this
30293 * @param {Roo.EventObject} e
30299 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
30302 * @cfg {String} title
30306 * @cfg {String} html
30310 * @cfg {String} bgimage
30314 * @cfg {String} videourl
30318 * @cfg {String} cls
30322 * @cfg {String} href
30326 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30331 * @cfg {String} (center|bottom) placetitle
30335 getAutoCreate : function()
30337 var cls = 'masonry-brick';
30339 if(this.href.length){
30340 cls += ' masonry-brick-link';
30343 if(this.bgimage.length){
30344 cls += ' masonry-brick-image';
30348 cls += ' masonry-' + this.size + '-brick';
30351 if(this.placetitle.length){
30353 switch (this.placetitle) {
30355 cls += ' masonry-center-title';
30358 cls += ' masonry-bottom-title';
30365 if(!this.html.length && !this.bgimage.length){
30366 cls += ' masonry-center-title';
30369 if(!this.html.length && this.bgimage.length){
30370 cls += ' masonry-bottom-title';
30375 cls += ' ' + this.cls;
30379 tag: (this.href.length) ? 'a' : 'div',
30384 cls: 'masonry-brick-paragraph',
30390 if(this.href.length){
30391 cfg.href = this.href;
30394 var cn = cfg.cn[0].cn;
30396 if(this.title.length){
30399 cls: 'masonry-brick-title',
30404 if(this.html.length){
30407 cls: 'masonry-brick-text',
30411 if (!this.title.length && !this.html.length) {
30412 cfg.cn[0].cls += ' hide';
30415 if(this.bgimage.length){
30418 cls: 'masonry-brick-image-view',
30422 if(this.videourl.length){
30423 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30424 // youtube support only?
30427 cls: 'masonry-brick-image-view',
30430 allowfullscreen : true
30439 initEvents: function()
30441 switch (this.size) {
30443 // this.intSize = 1;
30448 // this.intSize = 2;
30455 // this.intSize = 3;
30460 // this.intSize = 3;
30465 // this.intSize = 3;
30470 // this.intSize = 3;
30482 this.el.on('touchstart', this.onTouchStart, this);
30483 this.el.on('touchmove', this.onTouchMove, this);
30484 this.el.on('touchend', this.onTouchEnd, this);
30485 this.el.on('contextmenu', this.onContextMenu, this);
30487 this.el.on('mouseenter' ,this.enter, this);
30488 this.el.on('mouseleave', this.leave, this);
30491 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30492 this.parent().bricks.push(this);
30497 onClick: function(e, el)
30503 var time = this.endTimer - this.startTimer;
30511 e.preventDefault();
30514 enter: function(e, el)
30516 e.preventDefault();
30518 if(this.bgimage.length && this.html.length){
30519 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30523 leave: function(e, el)
30525 e.preventDefault();
30527 if(this.bgimage.length && this.html.length){
30528 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30532 onTouchStart: function(e, el)
30534 // e.preventDefault();
30536 this.touchmoved = false;
30538 if(!this.bgimage.length || !this.html.length){
30542 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30544 this.timer = new Date().getTime();
30548 onTouchMove: function(e, el)
30550 this.touchmoved = true;
30553 onContextMenu : function(e,el)
30555 e.preventDefault();
30556 e.stopPropagation();
30560 onTouchEnd: function(e, el)
30562 // e.preventDefault();
30564 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30571 if(!this.bgimage.length || !this.html.length){
30573 if(this.href.length){
30574 window.location.href = this.href;
30580 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30582 window.location.href = this.href;
30597 * @class Roo.bootstrap.Brick
30598 * @extends Roo.bootstrap.Component
30599 * Bootstrap Brick class
30602 * Create a new Brick
30603 * @param {Object} config The config object
30606 Roo.bootstrap.Brick = function(config){
30607 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30613 * When a Brick is click
30614 * @param {Roo.bootstrap.Brick} this
30615 * @param {Roo.EventObject} e
30621 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
30624 * @cfg {String} title
30628 * @cfg {String} html
30632 * @cfg {String} bgimage
30636 * @cfg {String} cls
30640 * @cfg {String} href
30644 * @cfg {String} video
30648 * @cfg {Boolean} square
30652 getAutoCreate : function()
30654 var cls = 'roo-brick';
30656 if(this.href.length){
30657 cls += ' roo-brick-link';
30660 if(this.bgimage.length){
30661 cls += ' roo-brick-image';
30664 if(!this.html.length && !this.bgimage.length){
30665 cls += ' roo-brick-center-title';
30668 if(!this.html.length && this.bgimage.length){
30669 cls += ' roo-brick-bottom-title';
30673 cls += ' ' + this.cls;
30677 tag: (this.href.length) ? 'a' : 'div',
30682 cls: 'roo-brick-paragraph',
30688 if(this.href.length){
30689 cfg.href = this.href;
30692 var cn = cfg.cn[0].cn;
30694 if(this.title.length){
30697 cls: 'roo-brick-title',
30702 if(this.html.length){
30705 cls: 'roo-brick-text',
30712 if(this.bgimage.length){
30715 cls: 'roo-brick-image-view',
30723 initEvents: function()
30725 if(this.title.length || this.html.length){
30726 this.el.on('mouseenter' ,this.enter, this);
30727 this.el.on('mouseleave', this.leave, this);
30731 Roo.EventManager.onWindowResize(this.resize, this);
30736 resize : function()
30738 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30740 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30741 // paragraph.setHeight(paragraph.getWidth());
30743 if(this.bgimage.length){
30744 var image = this.el.select('.roo-brick-image-view', true).first();
30745 image.setWidth(paragraph.getWidth());
30746 image.setHeight(paragraph.getWidth());
30751 enter: function(e, el)
30753 e.preventDefault();
30755 if(this.bgimage.length){
30756 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30757 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30761 leave: function(e, el)
30763 e.preventDefault();
30765 if(this.bgimage.length){
30766 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30767 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30777 * Ext JS Library 1.1.1
30778 * Copyright(c) 2006-2007, Ext JS, LLC.
30780 * Originally Released Under LGPL - original licence link has changed is not relivant.
30783 * <script type="text/javascript">
30788 * @class Roo.bootstrap.SplitBar
30789 * @extends Roo.util.Observable
30790 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
30794 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
30795 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
30796 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
30797 split.minSize = 100;
30798 split.maxSize = 600;
30799 split.animate = true;
30800 split.on('moved', splitterMoved);
30803 * Create a new SplitBar
30804 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
30805 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
30806 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30807 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
30808 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
30809 position of the SplitBar).
30811 Roo.bootstrap.SplitBar = function(cfg){
30816 // dragElement : elm
30817 // resizingElement: el,
30819 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
30820 // placement : Roo.bootstrap.SplitBar.LEFT ,
30821 // existingProxy ???
30824 this.el = Roo.get(cfg.dragElement, true);
30825 this.el.dom.unselectable = "on";
30827 this.resizingEl = Roo.get(cfg.resizingElement, true);
30831 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30832 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
30835 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
30838 * The minimum size of the resizing element. (Defaults to 0)
30844 * The maximum size of the resizing element. (Defaults to 2000)
30847 this.maxSize = 2000;
30850 * Whether to animate the transition to the new size
30853 this.animate = false;
30856 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
30859 this.useShim = false;
30864 if(!cfg.existingProxy){
30866 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
30868 this.proxy = Roo.get(cfg.existingProxy).dom;
30871 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
30874 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
30877 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
30880 this.dragSpecs = {};
30883 * @private The adapter to use to positon and resize elements
30885 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30886 this.adapter.init(this);
30888 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30890 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
30891 this.el.addClass("roo-splitbar-h");
30894 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
30895 this.el.addClass("roo-splitbar-v");
30901 * Fires when the splitter is moved (alias for {@link #event-moved})
30902 * @param {Roo.bootstrap.SplitBar} this
30903 * @param {Number} newSize the new width or height
30908 * Fires when the splitter is moved
30909 * @param {Roo.bootstrap.SplitBar} this
30910 * @param {Number} newSize the new width or height
30914 * @event beforeresize
30915 * Fires before the splitter is dragged
30916 * @param {Roo.bootstrap.SplitBar} this
30918 "beforeresize" : true,
30920 "beforeapply" : true
30923 Roo.util.Observable.call(this);
30926 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
30927 onStartProxyDrag : function(x, y){
30928 this.fireEvent("beforeresize", this);
30930 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
30932 o.enableDisplayMode("block");
30933 // all splitbars share the same overlay
30934 Roo.bootstrap.SplitBar.prototype.overlay = o;
30936 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30937 this.overlay.show();
30938 Roo.get(this.proxy).setDisplayed("block");
30939 var size = this.adapter.getElementSize(this);
30940 this.activeMinSize = this.getMinimumSize();;
30941 this.activeMaxSize = this.getMaximumSize();;
30942 var c1 = size - this.activeMinSize;
30943 var c2 = Math.max(this.activeMaxSize - size, 0);
30944 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30945 this.dd.resetConstraints();
30946 this.dd.setXConstraint(
30947 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
30948 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
30950 this.dd.setYConstraint(0, 0);
30952 this.dd.resetConstraints();
30953 this.dd.setXConstraint(0, 0);
30954 this.dd.setYConstraint(
30955 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
30956 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
30959 this.dragSpecs.startSize = size;
30960 this.dragSpecs.startPoint = [x, y];
30961 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
30965 * @private Called after the drag operation by the DDProxy
30967 onEndProxyDrag : function(e){
30968 Roo.get(this.proxy).setDisplayed(false);
30969 var endPoint = Roo.lib.Event.getXY(e);
30971 this.overlay.hide();
30974 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30975 newSize = this.dragSpecs.startSize +
30976 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
30977 endPoint[0] - this.dragSpecs.startPoint[0] :
30978 this.dragSpecs.startPoint[0] - endPoint[0]
30981 newSize = this.dragSpecs.startSize +
30982 (this.placement == Roo.bootstrap.SplitBar.TOP ?
30983 endPoint[1] - this.dragSpecs.startPoint[1] :
30984 this.dragSpecs.startPoint[1] - endPoint[1]
30987 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
30988 if(newSize != this.dragSpecs.startSize){
30989 if(this.fireEvent('beforeapply', this, newSize) !== false){
30990 this.adapter.setElementSize(this, newSize);
30991 this.fireEvent("moved", this, newSize);
30992 this.fireEvent("resize", this, newSize);
30998 * Get the adapter this SplitBar uses
30999 * @return The adapter object
31001 getAdapter : function(){
31002 return this.adapter;
31006 * Set the adapter this SplitBar uses
31007 * @param {Object} adapter A SplitBar adapter object
31009 setAdapter : function(adapter){
31010 this.adapter = adapter;
31011 this.adapter.init(this);
31015 * Gets the minimum size for the resizing element
31016 * @return {Number} The minimum size
31018 getMinimumSize : function(){
31019 return this.minSize;
31023 * Sets the minimum size for the resizing element
31024 * @param {Number} minSize The minimum size
31026 setMinimumSize : function(minSize){
31027 this.minSize = minSize;
31031 * Gets the maximum size for the resizing element
31032 * @return {Number} The maximum size
31034 getMaximumSize : function(){
31035 return this.maxSize;
31039 * Sets the maximum size for the resizing element
31040 * @param {Number} maxSize The maximum size
31042 setMaximumSize : function(maxSize){
31043 this.maxSize = maxSize;
31047 * Sets the initialize size for the resizing element
31048 * @param {Number} size The initial size
31050 setCurrentSize : function(size){
31051 var oldAnimate = this.animate;
31052 this.animate = false;
31053 this.adapter.setElementSize(this, size);
31054 this.animate = oldAnimate;
31058 * Destroy this splitbar.
31059 * @param {Boolean} removeEl True to remove the element
31061 destroy : function(removeEl){
31063 this.shim.remove();
31066 this.proxy.parentNode.removeChild(this.proxy);
31074 * @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.
31076 Roo.bootstrap.SplitBar.createProxy = function(dir){
31077 var proxy = new Roo.Element(document.createElement("div"));
31078 proxy.unselectable();
31079 var cls = 'roo-splitbar-proxy';
31080 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31081 document.body.appendChild(proxy.dom);
31086 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31087 * Default Adapter. It assumes the splitter and resizing element are not positioned
31088 * elements and only gets/sets the width of the element. Generally used for table based layouts.
31090 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31093 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31094 // do nothing for now
31095 init : function(s){
31099 * Called before drag operations to get the current size of the resizing element.
31100 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31102 getElementSize : function(s){
31103 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31104 return s.resizingEl.getWidth();
31106 return s.resizingEl.getHeight();
31111 * Called after drag operations to set the size of the resizing element.
31112 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31113 * @param {Number} newSize The new size to set
31114 * @param {Function} onComplete A function to be invoked when resizing is complete
31116 setElementSize : function(s, newSize, onComplete){
31117 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31119 s.resizingEl.setWidth(newSize);
31121 onComplete(s, newSize);
31124 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31129 s.resizingEl.setHeight(newSize);
31131 onComplete(s, newSize);
31134 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31141 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31142 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31143 * Adapter that moves the splitter element to align with the resized sizing element.
31144 * Used with an absolute positioned SplitBar.
31145 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31146 * document.body, make sure you assign an id to the body element.
31148 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31149 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31150 this.container = Roo.get(container);
31153 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31154 init : function(s){
31155 this.basic.init(s);
31158 getElementSize : function(s){
31159 return this.basic.getElementSize(s);
31162 setElementSize : function(s, newSize, onComplete){
31163 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31166 moveSplitter : function(s){
31167 var yes = Roo.bootstrap.SplitBar;
31168 switch(s.placement){
31170 s.el.setX(s.resizingEl.getRight());
31173 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31176 s.el.setY(s.resizingEl.getBottom());
31179 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31186 * Orientation constant - Create a vertical SplitBar
31190 Roo.bootstrap.SplitBar.VERTICAL = 1;
31193 * Orientation constant - Create a horizontal SplitBar
31197 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31200 * Placement constant - The resizing element is to the left of the splitter element
31204 Roo.bootstrap.SplitBar.LEFT = 1;
31207 * Placement constant - The resizing element is to the right of the splitter element
31211 Roo.bootstrap.SplitBar.RIGHT = 2;
31214 * Placement constant - The resizing element is positioned above the splitter element
31218 Roo.bootstrap.SplitBar.TOP = 3;
31221 * Placement constant - The resizing element is positioned under splitter element
31225 Roo.bootstrap.SplitBar.BOTTOM = 4;
31226 Roo.namespace("Roo.bootstrap.layout");/*
31228 * Ext JS Library 1.1.1
31229 * Copyright(c) 2006-2007, Ext JS, LLC.
31231 * Originally Released Under LGPL - original licence link has changed is not relivant.
31234 * <script type="text/javascript">
31238 * @class Roo.bootstrap.layout.Manager
31239 * @extends Roo.bootstrap.Component
31240 * Base class for layout managers.
31242 Roo.bootstrap.layout.Manager = function(config)
31244 Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31250 /** false to disable window resize monitoring @type Boolean */
31251 this.monitorWindowResize = true;
31256 * Fires when a layout is performed.
31257 * @param {Roo.LayoutManager} this
31261 * @event regionresized
31262 * Fires when the user resizes a region.
31263 * @param {Roo.LayoutRegion} region The resized region
31264 * @param {Number} newSize The new size (width for east/west, height for north/south)
31266 "regionresized" : true,
31268 * @event regioncollapsed
31269 * Fires when a region is collapsed.
31270 * @param {Roo.LayoutRegion} region The collapsed region
31272 "regioncollapsed" : true,
31274 * @event regionexpanded
31275 * Fires when a region is expanded.
31276 * @param {Roo.LayoutRegion} region The expanded region
31278 "regionexpanded" : true
31280 this.updating = false;
31283 this.el = Roo.get(config.el);
31289 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31294 monitorWindowResize : true,
31300 onRender : function(ct, position)
31303 this.el = Roo.get(ct);
31309 initEvents: function()
31313 // ie scrollbar fix
31314 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31315 document.body.scroll = "no";
31316 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31317 this.el.position('relative');
31319 this.id = this.el.id;
31320 this.el.addClass("roo-layout-container");
31321 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31322 if(this.el.dom != document.body ) {
31323 this.el.on('resize', this.layout,this);
31324 this.el.on('show', this.layout,this);
31330 * Returns true if this layout is currently being updated
31331 * @return {Boolean}
31333 isUpdating : function(){
31334 return this.updating;
31338 * Suspend the LayoutManager from doing auto-layouts while
31339 * making multiple add or remove calls
31341 beginUpdate : function(){
31342 this.updating = true;
31346 * Restore auto-layouts and optionally disable the manager from performing a layout
31347 * @param {Boolean} noLayout true to disable a layout update
31349 endUpdate : function(noLayout){
31350 this.updating = false;
31356 layout: function(){
31360 onRegionResized : function(region, newSize){
31361 this.fireEvent("regionresized", region, newSize);
31365 onRegionCollapsed : function(region){
31366 this.fireEvent("regioncollapsed", region);
31369 onRegionExpanded : function(region){
31370 this.fireEvent("regionexpanded", region);
31374 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31375 * performs box-model adjustments.
31376 * @return {Object} The size as an object {width: (the width), height: (the height)}
31378 getViewSize : function()
31381 if(this.el.dom != document.body){
31382 size = this.el.getSize();
31384 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31386 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31387 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31392 * Returns the Element this layout is bound to.
31393 * @return {Roo.Element}
31395 getEl : function(){
31400 * Returns the specified region.
31401 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31402 * @return {Roo.LayoutRegion}
31404 getRegion : function(target){
31405 return this.regions[target.toLowerCase()];
31408 onWindowResize : function(){
31409 if(this.monitorWindowResize){
31415 * Ext JS Library 1.1.1
31416 * Copyright(c) 2006-2007, Ext JS, LLC.
31418 * Originally Released Under LGPL - original licence link has changed is not relivant.
31421 * <script type="text/javascript">
31424 * @class Roo.bootstrap.layout.Border
31425 * @extends Roo.bootstrap.layout.Manager
31426 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31427 * please see: examples/bootstrap/nested.html<br><br>
31429 <b>The container the layout is rendered into can be either the body element or any other element.
31430 If it is not the body element, the container needs to either be an absolute positioned element,
31431 or you will need to add "position:relative" to the css of the container. You will also need to specify
31432 the container size if it is not the body element.</b>
31435 * Create a new Border
31436 * @param {Object} config Configuration options
31438 Roo.bootstrap.layout.Border = function(config){
31439 config = config || {};
31440 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31444 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31445 if(config[region]){
31446 config[region].region = region;
31447 this.addRegion(config[region]);
31453 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
31455 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31457 * Creates and adds a new region if it doesn't already exist.
31458 * @param {String} target The target region key (north, south, east, west or center).
31459 * @param {Object} config The regions config object
31460 * @return {BorderLayoutRegion} The new region
31462 addRegion : function(config)
31464 if(!this.regions[config.region]){
31465 var r = this.factory(config);
31466 this.bindRegion(r);
31468 return this.regions[config.region];
31472 bindRegion : function(r){
31473 this.regions[r.config.region] = r;
31475 r.on("visibilitychange", this.layout, this);
31476 r.on("paneladded", this.layout, this);
31477 r.on("panelremoved", this.layout, this);
31478 r.on("invalidated", this.layout, this);
31479 r.on("resized", this.onRegionResized, this);
31480 r.on("collapsed", this.onRegionCollapsed, this);
31481 r.on("expanded", this.onRegionExpanded, this);
31485 * Performs a layout update.
31487 layout : function()
31489 if(this.updating) {
31492 var size = this.getViewSize();
31493 var w = size.width;
31494 var h = size.height;
31499 //var x = 0, y = 0;
31501 var rs = this.regions;
31502 var north = rs["north"];
31503 var south = rs["south"];
31504 var west = rs["west"];
31505 var east = rs["east"];
31506 var center = rs["center"];
31507 //if(this.hideOnLayout){ // not supported anymore
31508 //c.el.setStyle("display", "none");
31510 if(north && north.isVisible()){
31511 var b = north.getBox();
31512 var m = north.getMargins();
31513 b.width = w - (m.left+m.right);
31516 centerY = b.height + b.y + m.bottom;
31517 centerH -= centerY;
31518 north.updateBox(this.safeBox(b));
31520 if(south && south.isVisible()){
31521 var b = south.getBox();
31522 var m = south.getMargins();
31523 b.width = w - (m.left+m.right);
31525 var totalHeight = (b.height + m.top + m.bottom);
31526 b.y = h - totalHeight + m.top;
31527 centerH -= totalHeight;
31528 south.updateBox(this.safeBox(b));
31530 if(west && west.isVisible()){
31531 var b = west.getBox();
31532 var m = west.getMargins();
31533 b.height = centerH - (m.top+m.bottom);
31535 b.y = centerY + m.top;
31536 var totalWidth = (b.width + m.left + m.right);
31537 centerX += totalWidth;
31538 centerW -= totalWidth;
31539 west.updateBox(this.safeBox(b));
31541 if(east && east.isVisible()){
31542 var b = east.getBox();
31543 var m = east.getMargins();
31544 b.height = centerH - (m.top+m.bottom);
31545 var totalWidth = (b.width + m.left + m.right);
31546 b.x = w - totalWidth + m.left;
31547 b.y = centerY + m.top;
31548 centerW -= totalWidth;
31549 east.updateBox(this.safeBox(b));
31552 var m = center.getMargins();
31554 x: centerX + m.left,
31555 y: centerY + m.top,
31556 width: centerW - (m.left+m.right),
31557 height: centerH - (m.top+m.bottom)
31559 //if(this.hideOnLayout){
31560 //center.el.setStyle("display", "block");
31562 center.updateBox(this.safeBox(centerBox));
31565 this.fireEvent("layout", this);
31569 safeBox : function(box){
31570 box.width = Math.max(0, box.width);
31571 box.height = Math.max(0, box.height);
31576 * Adds a ContentPanel (or subclass) to this layout.
31577 * @param {String} target The target region key (north, south, east, west or center).
31578 * @param {Roo.ContentPanel} panel The panel to add
31579 * @return {Roo.ContentPanel} The added panel
31581 add : function(target, panel){
31583 target = target.toLowerCase();
31584 return this.regions[target].add(panel);
31588 * Remove a ContentPanel (or subclass) to this layout.
31589 * @param {String} target The target region key (north, south, east, west or center).
31590 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31591 * @return {Roo.ContentPanel} The removed panel
31593 remove : function(target, panel){
31594 target = target.toLowerCase();
31595 return this.regions[target].remove(panel);
31599 * Searches all regions for a panel with the specified id
31600 * @param {String} panelId
31601 * @return {Roo.ContentPanel} The panel or null if it wasn't found
31603 findPanel : function(panelId){
31604 var rs = this.regions;
31605 for(var target in rs){
31606 if(typeof rs[target] != "function"){
31607 var p = rs[target].getPanel(panelId);
31617 * Searches all regions for a panel with the specified id and activates (shows) it.
31618 * @param {String/ContentPanel} panelId The panels id or the panel itself
31619 * @return {Roo.ContentPanel} The shown panel or null
31621 showPanel : function(panelId) {
31622 var rs = this.regions;
31623 for(var target in rs){
31624 var r = rs[target];
31625 if(typeof r != "function"){
31626 if(r.hasPanel(panelId)){
31627 return r.showPanel(panelId);
31635 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31636 * @param {Roo.state.Provider} provider (optional) An alternate state provider
31639 restoreState : function(provider){
31641 provider = Roo.state.Manager;
31643 var sm = new Roo.LayoutStateManager();
31644 sm.init(this, provider);
31650 * Adds a xtype elements to the layout.
31654 xtype : 'ContentPanel',
31661 xtype : 'NestedLayoutPanel',
31667 items : [ ... list of content panels or nested layout panels.. ]
31671 * @param {Object} cfg Xtype definition of item to add.
31673 addxtype : function(cfg)
31675 // basically accepts a pannel...
31676 // can accept a layout region..!?!?
31677 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31680 // theory? children can only be panels??
31682 //if (!cfg.xtype.match(/Panel$/)) {
31687 if (typeof(cfg.region) == 'undefined') {
31688 Roo.log("Failed to add Panel, region was not set");
31692 var region = cfg.region;
31698 xitems = cfg.items;
31705 case 'Content': // ContentPanel (el, cfg)
31706 case 'Scroll': // ContentPanel (el, cfg)
31708 cfg.autoCreate = true;
31709 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31711 // var el = this.el.createChild();
31712 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31715 this.add(region, ret);
31719 case 'TreePanel': // our new panel!
31720 cfg.el = this.el.createChild();
31721 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31722 this.add(region, ret);
31727 // create a new Layout (which is a Border Layout...
31729 var clayout = cfg.layout;
31730 clayout.el = this.el.createChild();
31731 clayout.items = clayout.items || [];
31735 // replace this exitems with the clayout ones..
31736 xitems = clayout.items;
31738 // force background off if it's in center...
31739 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31740 cfg.background = false;
31742 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
31745 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31746 //console.log('adding nested layout panel ' + cfg.toSource());
31747 this.add(region, ret);
31748 nb = {}; /// find first...
31753 // needs grid and region
31755 //var el = this.getRegion(region).el.createChild();
31757 *var el = this.el.createChild();
31758 // create the grid first...
31759 cfg.grid.container = el;
31760 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31763 if (region == 'center' && this.active ) {
31764 cfg.background = false;
31767 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31769 this.add(region, ret);
31771 if (cfg.background) {
31772 // render grid on panel activation (if panel background)
31773 ret.on('activate', function(gp) {
31774 if (!gp.grid.rendered) {
31775 // gp.grid.render(el);
31779 // cfg.grid.render(el);
31785 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31786 // it was the old xcomponent building that caused this before.
31787 // espeically if border is the top element in the tree.
31797 if (typeof(Roo[cfg.xtype]) != 'undefined') {
31799 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31800 this.add(region, ret);
31804 throw "Can not add '" + cfg.xtype + "' to Border";
31810 this.beginUpdate();
31814 Roo.each(xitems, function(i) {
31815 region = nb && i.region ? i.region : false;
31817 var add = ret.addxtype(i);
31820 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31821 if (!i.background) {
31822 abn[region] = nb[region] ;
31829 // make the last non-background panel active..
31830 //if (nb) { Roo.log(abn); }
31833 for(var r in abn) {
31834 region = this.getRegion(r);
31836 // tried using nb[r], but it does not work..
31838 region.showPanel(abn[r]);
31849 factory : function(cfg)
31852 var validRegions = Roo.bootstrap.layout.Border.regions;
31854 var target = cfg.region;
31857 var r = Roo.bootstrap.layout;
31861 return new r.North(cfg);
31863 return new r.South(cfg);
31865 return new r.East(cfg);
31867 return new r.West(cfg);
31869 return new r.Center(cfg);
31871 throw 'Layout region "'+target+'" not supported.';
31878 * Ext JS Library 1.1.1
31879 * Copyright(c) 2006-2007, Ext JS, LLC.
31881 * Originally Released Under LGPL - original licence link has changed is not relivant.
31884 * <script type="text/javascript">
31888 * @class Roo.bootstrap.layout.Basic
31889 * @extends Roo.util.Observable
31890 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31891 * and does not have a titlebar, tabs or any other features. All it does is size and position
31892 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31893 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
31894 * @cfg {string} region the region that it inhabits..
31895 * @cfg {bool} skipConfig skip config?
31899 Roo.bootstrap.layout.Basic = function(config){
31901 this.mgr = config.mgr;
31903 this.position = config.region;
31905 var skipConfig = config.skipConfig;
31909 * @scope Roo.BasicLayoutRegion
31913 * @event beforeremove
31914 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31915 * @param {Roo.LayoutRegion} this
31916 * @param {Roo.ContentPanel} panel The panel
31917 * @param {Object} e The cancel event object
31919 "beforeremove" : true,
31921 * @event invalidated
31922 * Fires when the layout for this region is changed.
31923 * @param {Roo.LayoutRegion} this
31925 "invalidated" : true,
31927 * @event visibilitychange
31928 * Fires when this region is shown or hidden
31929 * @param {Roo.LayoutRegion} this
31930 * @param {Boolean} visibility true or false
31932 "visibilitychange" : true,
31934 * @event paneladded
31935 * Fires when a panel is added.
31936 * @param {Roo.LayoutRegion} this
31937 * @param {Roo.ContentPanel} panel The panel
31939 "paneladded" : true,
31941 * @event panelremoved
31942 * Fires when a panel is removed.
31943 * @param {Roo.LayoutRegion} this
31944 * @param {Roo.ContentPanel} panel The panel
31946 "panelremoved" : true,
31948 * @event beforecollapse
31949 * Fires when this region before collapse.
31950 * @param {Roo.LayoutRegion} this
31952 "beforecollapse" : true,
31955 * Fires when this region is collapsed.
31956 * @param {Roo.LayoutRegion} this
31958 "collapsed" : true,
31961 * Fires when this region is expanded.
31962 * @param {Roo.LayoutRegion} this
31967 * Fires when this region is slid into view.
31968 * @param {Roo.LayoutRegion} this
31970 "slideshow" : true,
31973 * Fires when this region slides out of view.
31974 * @param {Roo.LayoutRegion} this
31976 "slidehide" : true,
31978 * @event panelactivated
31979 * Fires when a panel is activated.
31980 * @param {Roo.LayoutRegion} this
31981 * @param {Roo.ContentPanel} panel The activated panel
31983 "panelactivated" : true,
31986 * Fires when the user resizes this region.
31987 * @param {Roo.LayoutRegion} this
31988 * @param {Number} newSize The new size (width for east/west, height for north/south)
31992 /** A collection of panels in this region. @type Roo.util.MixedCollection */
31993 this.panels = new Roo.util.MixedCollection();
31994 this.panels.getKey = this.getPanelId.createDelegate(this);
31996 this.activePanel = null;
31997 // ensure listeners are added...
31999 if (config.listeners || config.events) {
32000 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32001 listeners : config.listeners || {},
32002 events : config.events || {}
32006 if(skipConfig !== true){
32007 this.applyConfig(config);
32011 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32013 getPanelId : function(p){
32017 applyConfig : function(config){
32018 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32019 this.config = config;
32024 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
32025 * the width, for horizontal (north, south) the height.
32026 * @param {Number} newSize The new width or height
32028 resizeTo : function(newSize){
32029 var el = this.el ? this.el :
32030 (this.activePanel ? this.activePanel.getEl() : null);
32032 switch(this.position){
32035 el.setWidth(newSize);
32036 this.fireEvent("resized", this, newSize);
32040 el.setHeight(newSize);
32041 this.fireEvent("resized", this, newSize);
32047 getBox : function(){
32048 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32051 getMargins : function(){
32052 return this.margins;
32055 updateBox : function(box){
32057 var el = this.activePanel.getEl();
32058 el.dom.style.left = box.x + "px";
32059 el.dom.style.top = box.y + "px";
32060 this.activePanel.setSize(box.width, box.height);
32064 * Returns the container element for this region.
32065 * @return {Roo.Element}
32067 getEl : function(){
32068 return this.activePanel;
32072 * Returns true if this region is currently visible.
32073 * @return {Boolean}
32075 isVisible : function(){
32076 return this.activePanel ? true : false;
32079 setActivePanel : function(panel){
32080 panel = this.getPanel(panel);
32081 if(this.activePanel && this.activePanel != panel){
32082 this.activePanel.setActiveState(false);
32083 this.activePanel.getEl().setLeftTop(-10000,-10000);
32085 this.activePanel = panel;
32086 panel.setActiveState(true);
32088 panel.setSize(this.box.width, this.box.height);
32090 this.fireEvent("panelactivated", this, panel);
32091 this.fireEvent("invalidated");
32095 * Show the specified panel.
32096 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32097 * @return {Roo.ContentPanel} The shown panel or null
32099 showPanel : function(panel){
32100 panel = this.getPanel(panel);
32102 this.setActivePanel(panel);
32108 * Get the active panel for this region.
32109 * @return {Roo.ContentPanel} The active panel or null
32111 getActivePanel : function(){
32112 return this.activePanel;
32116 * Add the passed ContentPanel(s)
32117 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32118 * @return {Roo.ContentPanel} The panel added (if only one was added)
32120 add : function(panel){
32121 if(arguments.length > 1){
32122 for(var i = 0, len = arguments.length; i < len; i++) {
32123 this.add(arguments[i]);
32127 if(this.hasPanel(panel)){
32128 this.showPanel(panel);
32131 var el = panel.getEl();
32132 if(el.dom.parentNode != this.mgr.el.dom){
32133 this.mgr.el.dom.appendChild(el.dom);
32135 if(panel.setRegion){
32136 panel.setRegion(this);
32138 this.panels.add(panel);
32139 el.setStyle("position", "absolute");
32140 if(!panel.background){
32141 this.setActivePanel(panel);
32142 if(this.config.initialSize && this.panels.getCount()==1){
32143 this.resizeTo(this.config.initialSize);
32146 this.fireEvent("paneladded", this, panel);
32151 * Returns true if the panel is in this region.
32152 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32153 * @return {Boolean}
32155 hasPanel : function(panel){
32156 if(typeof panel == "object"){ // must be panel obj
32157 panel = panel.getId();
32159 return this.getPanel(panel) ? true : false;
32163 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32164 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32165 * @param {Boolean} preservePanel Overrides the config preservePanel option
32166 * @return {Roo.ContentPanel} The panel that was removed
32168 remove : function(panel, preservePanel){
32169 panel = this.getPanel(panel);
32174 this.fireEvent("beforeremove", this, panel, e);
32175 if(e.cancel === true){
32178 var panelId = panel.getId();
32179 this.panels.removeKey(panelId);
32184 * Returns the panel specified or null if it's not in this region.
32185 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32186 * @return {Roo.ContentPanel}
32188 getPanel : function(id){
32189 if(typeof id == "object"){ // must be panel obj
32192 return this.panels.get(id);
32196 * Returns this regions position (north/south/east/west/center).
32199 getPosition: function(){
32200 return this.position;
32204 * Ext JS Library 1.1.1
32205 * Copyright(c) 2006-2007, Ext JS, LLC.
32207 * Originally Released Under LGPL - original licence link has changed is not relivant.
32210 * <script type="text/javascript">
32214 * @class Roo.bootstrap.layout.Region
32215 * @extends Roo.bootstrap.layout.Basic
32216 * This class represents a region in a layout manager.
32218 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32219 * @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})
32220 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
32221 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
32222 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
32223 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
32224 * @cfg {String} title The title for the region (overrides panel titles)
32225 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
32226 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32227 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
32228 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32229 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
32230 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32231 * the space available, similar to FireFox 1.5 tabs (defaults to false)
32232 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
32233 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
32234 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
32236 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
32237 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
32238 * @cfg {Boolean} disableTabTips True to disable tab tooltips
32239 * @cfg {Number} width For East/West panels
32240 * @cfg {Number} height For North/South panels
32241 * @cfg {Boolean} split To show the splitter
32242 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
32244 * @cfg {string} cls Extra CSS classes to add to region
32246 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32247 * @cfg {string} region the region that it inhabits..
32250 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
32251 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
32253 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
32254 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
32255 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
32257 Roo.bootstrap.layout.Region = function(config)
32259 this.applyConfig(config);
32261 var mgr = config.mgr;
32262 var pos = config.region;
32263 config.skipConfig = true;
32264 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32267 this.onRender(mgr.el);
32270 this.visible = true;
32271 this.collapsed = false;
32274 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32276 position: '', // set by wrapper (eg. north/south etc..)
32278 createBody : function(){
32279 /** This region's body element
32280 * @type Roo.Element */
32281 this.bodyEl = this.el.createChild({
32283 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32287 onRender: function(ctr, pos)
32289 var dh = Roo.DomHelper;
32290 /** This region's container element
32291 * @type Roo.Element */
32292 this.el = dh.append(ctr.dom, {
32294 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32296 /** This region's title element
32297 * @type Roo.Element */
32299 this.titleEl = dh.append(this.el.dom,
32302 unselectable: "on",
32303 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32305 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
32306 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32309 this.titleEl.enableDisplayMode();
32310 /** This region's title text element
32311 * @type HTMLElement */
32312 this.titleTextEl = this.titleEl.dom.firstChild;
32313 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32315 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32316 this.closeBtn.enableDisplayMode();
32317 this.closeBtn.on("click", this.closeClicked, this);
32318 this.closeBtn.hide();
32320 this.createBody(this.config);
32321 if(this.config.hideWhenEmpty){
32323 this.on("paneladded", this.validateVisibility, this);
32324 this.on("panelremoved", this.validateVisibility, this);
32326 if(this.autoScroll){
32327 this.bodyEl.setStyle("overflow", "auto");
32329 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32331 //if(c.titlebar !== false){
32332 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32333 this.titleEl.hide();
32335 this.titleEl.show();
32336 if(this.config.title){
32337 this.titleTextEl.innerHTML = this.config.title;
32341 if(this.config.collapsed){
32342 this.collapse(true);
32344 if(this.config.hidden){
32349 applyConfig : function(c)
32352 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32353 var dh = Roo.DomHelper;
32354 if(c.titlebar !== false){
32355 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32356 this.collapseBtn.on("click", this.collapse, this);
32357 this.collapseBtn.enableDisplayMode();
32359 if(c.showPin === true || this.showPin){
32360 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32361 this.stickBtn.enableDisplayMode();
32362 this.stickBtn.on("click", this.expand, this);
32363 this.stickBtn.hide();
32368 /** This region's collapsed element
32369 * @type Roo.Element */
32372 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32373 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32376 if(c.floatable !== false){
32377 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32378 this.collapsedEl.on("click", this.collapseClick, this);
32381 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32382 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32383 id: "message", unselectable: "on", style:{"float":"left"}});
32384 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32386 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32387 this.expandBtn.on("click", this.expand, this);
32391 if(this.collapseBtn){
32392 this.collapseBtn.setVisible(c.collapsible == true);
32395 this.cmargins = c.cmargins || this.cmargins ||
32396 (this.position == "west" || this.position == "east" ?
32397 {top: 0, left: 2, right:2, bottom: 0} :
32398 {top: 2, left: 0, right:0, bottom: 2});
32400 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32403 this.bottomTabs = c.tabPosition != "top";
32405 this.autoScroll = c.autoScroll || false;
32410 this.duration = c.duration || .30;
32411 this.slideDuration = c.slideDuration || .45;
32416 * Returns true if this region is currently visible.
32417 * @return {Boolean}
32419 isVisible : function(){
32420 return this.visible;
32424 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32425 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
32427 //setCollapsedTitle : function(title){
32428 // title = title || " ";
32429 // if(this.collapsedTitleTextEl){
32430 // this.collapsedTitleTextEl.innerHTML = title;
32434 getBox : function(){
32436 // if(!this.collapsed){
32437 b = this.el.getBox(false, true);
32439 // b = this.collapsedEl.getBox(false, true);
32444 getMargins : function(){
32445 return this.margins;
32446 //return this.collapsed ? this.cmargins : this.margins;
32449 highlight : function(){
32450 this.el.addClass("x-layout-panel-dragover");
32453 unhighlight : function(){
32454 this.el.removeClass("x-layout-panel-dragover");
32457 updateBox : function(box)
32460 if(!this.collapsed){
32461 this.el.dom.style.left = box.x + "px";
32462 this.el.dom.style.top = box.y + "px";
32463 this.updateBody(box.width, box.height);
32465 this.collapsedEl.dom.style.left = box.x + "px";
32466 this.collapsedEl.dom.style.top = box.y + "px";
32467 this.collapsedEl.setSize(box.width, box.height);
32470 this.tabs.autoSizeTabs();
32474 updateBody : function(w, h)
32477 this.el.setWidth(w);
32478 w -= this.el.getBorderWidth("rl");
32479 if(this.config.adjustments){
32480 w += this.config.adjustments[0];
32484 this.el.setHeight(h);
32485 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32486 h -= this.el.getBorderWidth("tb");
32487 if(this.config.adjustments){
32488 h += this.config.adjustments[1];
32490 this.bodyEl.setHeight(h);
32492 h = this.tabs.syncHeight(h);
32495 if(this.panelSize){
32496 w = w !== null ? w : this.panelSize.width;
32497 h = h !== null ? h : this.panelSize.height;
32499 if(this.activePanel){
32500 var el = this.activePanel.getEl();
32501 w = w !== null ? w : el.getWidth();
32502 h = h !== null ? h : el.getHeight();
32503 this.panelSize = {width: w, height: h};
32504 this.activePanel.setSize(w, h);
32506 if(Roo.isIE && this.tabs){
32507 this.tabs.el.repaint();
32512 * Returns the container element for this region.
32513 * @return {Roo.Element}
32515 getEl : function(){
32520 * Hides this region.
32523 //if(!this.collapsed){
32524 this.el.dom.style.left = "-2000px";
32527 // this.collapsedEl.dom.style.left = "-2000px";
32528 // this.collapsedEl.hide();
32530 this.visible = false;
32531 this.fireEvent("visibilitychange", this, false);
32535 * Shows this region if it was previously hidden.
32538 //if(!this.collapsed){
32541 // this.collapsedEl.show();
32543 this.visible = true;
32544 this.fireEvent("visibilitychange", this, true);
32547 closeClicked : function(){
32548 if(this.activePanel){
32549 this.remove(this.activePanel);
32553 collapseClick : function(e){
32555 e.stopPropagation();
32558 e.stopPropagation();
32564 * Collapses this region.
32565 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32568 collapse : function(skipAnim, skipCheck = false){
32569 if(this.collapsed) {
32573 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32575 this.collapsed = true;
32577 this.split.el.hide();
32579 if(this.config.animate && skipAnim !== true){
32580 this.fireEvent("invalidated", this);
32581 this.animateCollapse();
32583 this.el.setLocation(-20000,-20000);
32585 this.collapsedEl.show();
32586 this.fireEvent("collapsed", this);
32587 this.fireEvent("invalidated", this);
32593 animateCollapse : function(){
32598 * Expands this region if it was previously collapsed.
32599 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32600 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32603 expand : function(e, skipAnim){
32605 e.stopPropagation();
32607 if(!this.collapsed || this.el.hasActiveFx()) {
32611 this.afterSlideIn();
32614 this.collapsed = false;
32615 if(this.config.animate && skipAnim !== true){
32616 this.animateExpand();
32620 this.split.el.show();
32622 this.collapsedEl.setLocation(-2000,-2000);
32623 this.collapsedEl.hide();
32624 this.fireEvent("invalidated", this);
32625 this.fireEvent("expanded", this);
32629 animateExpand : function(){
32633 initTabs : function()
32635 this.bodyEl.setStyle("overflow", "hidden");
32636 var ts = new Roo.bootstrap.panel.Tabs({
32637 el: this.bodyEl.dom,
32638 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32639 disableTooltips: this.config.disableTabTips,
32640 toolbar : this.config.toolbar
32643 if(this.config.hideTabs){
32644 ts.stripWrap.setDisplayed(false);
32647 ts.resizeTabs = this.config.resizeTabs === true;
32648 ts.minTabWidth = this.config.minTabWidth || 40;
32649 ts.maxTabWidth = this.config.maxTabWidth || 250;
32650 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32651 ts.monitorResize = false;
32652 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32653 ts.bodyEl.addClass('roo-layout-tabs-body');
32654 this.panels.each(this.initPanelAsTab, this);
32657 initPanelAsTab : function(panel){
32658 var ti = this.tabs.addTab(
32660 panel.getTitle(), null,
32661 this.config.closeOnTab && panel.isClosable()
32663 if(panel.tabTip !== undefined){
32664 ti.setTooltip(panel.tabTip);
32666 ti.on("activate", function(){
32667 this.setActivePanel(panel);
32670 if(this.config.closeOnTab){
32671 ti.on("beforeclose", function(t, e){
32673 this.remove(panel);
32679 updatePanelTitle : function(panel, title)
32681 if(this.activePanel == panel){
32682 this.updateTitle(title);
32685 var ti = this.tabs.getTab(panel.getEl().id);
32687 if(panel.tabTip !== undefined){
32688 ti.setTooltip(panel.tabTip);
32693 updateTitle : function(title){
32694 if(this.titleTextEl && !this.config.title){
32695 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
32699 setActivePanel : function(panel)
32701 panel = this.getPanel(panel);
32702 if(this.activePanel && this.activePanel != panel){
32703 this.activePanel.setActiveState(false);
32705 this.activePanel = panel;
32706 panel.setActiveState(true);
32707 if(this.panelSize){
32708 panel.setSize(this.panelSize.width, this.panelSize.height);
32711 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32713 this.updateTitle(panel.getTitle());
32715 this.fireEvent("invalidated", this);
32717 this.fireEvent("panelactivated", this, panel);
32721 * Shows the specified panel.
32722 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32723 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32725 showPanel : function(panel)
32727 panel = this.getPanel(panel);
32730 var tab = this.tabs.getTab(panel.getEl().id);
32731 if(tab.isHidden()){
32732 this.tabs.unhideTab(tab.id);
32736 this.setActivePanel(panel);
32743 * Get the active panel for this region.
32744 * @return {Roo.ContentPanel} The active panel or null
32746 getActivePanel : function(){
32747 return this.activePanel;
32750 validateVisibility : function(){
32751 if(this.panels.getCount() < 1){
32752 this.updateTitle(" ");
32753 this.closeBtn.hide();
32756 if(!this.isVisible()){
32763 * Adds the passed ContentPanel(s) to this region.
32764 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32765 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32767 add : function(panel){
32768 if(arguments.length > 1){
32769 for(var i = 0, len = arguments.length; i < len; i++) {
32770 this.add(arguments[i]);
32774 if(this.hasPanel(panel)){
32775 this.showPanel(panel);
32778 panel.setRegion(this);
32779 this.panels.add(panel);
32780 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32781 this.bodyEl.dom.appendChild(panel.getEl().dom);
32782 if(panel.background !== true){
32783 this.setActivePanel(panel);
32785 this.fireEvent("paneladded", this, panel);
32791 this.initPanelAsTab(panel);
32795 if(panel.background !== true){
32796 this.tabs.activate(panel.getEl().id);
32798 this.fireEvent("paneladded", this, panel);
32803 * Hides the tab for the specified panel.
32804 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32806 hidePanel : function(panel){
32807 if(this.tabs && (panel = this.getPanel(panel))){
32808 this.tabs.hideTab(panel.getEl().id);
32813 * Unhides the tab for a previously hidden panel.
32814 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32816 unhidePanel : function(panel){
32817 if(this.tabs && (panel = this.getPanel(panel))){
32818 this.tabs.unhideTab(panel.getEl().id);
32822 clearPanels : function(){
32823 while(this.panels.getCount() > 0){
32824 this.remove(this.panels.first());
32829 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32830 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32831 * @param {Boolean} preservePanel Overrides the config preservePanel option
32832 * @return {Roo.ContentPanel} The panel that was removed
32834 remove : function(panel, preservePanel)
32836 panel = this.getPanel(panel);
32841 this.fireEvent("beforeremove", this, panel, e);
32842 if(e.cancel === true){
32845 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32846 var panelId = panel.getId();
32847 this.panels.removeKey(panelId);
32849 document.body.appendChild(panel.getEl().dom);
32852 this.tabs.removeTab(panel.getEl().id);
32853 }else if (!preservePanel){
32854 this.bodyEl.dom.removeChild(panel.getEl().dom);
32856 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32857 var p = this.panels.first();
32858 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32859 tempEl.appendChild(p.getEl().dom);
32860 this.bodyEl.update("");
32861 this.bodyEl.dom.appendChild(p.getEl().dom);
32863 this.updateTitle(p.getTitle());
32865 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32866 this.setActivePanel(p);
32868 panel.setRegion(null);
32869 if(this.activePanel == panel){
32870 this.activePanel = null;
32872 if(this.config.autoDestroy !== false && preservePanel !== true){
32873 try{panel.destroy();}catch(e){}
32875 this.fireEvent("panelremoved", this, panel);
32880 * Returns the TabPanel component used by this region
32881 * @return {Roo.TabPanel}
32883 getTabs : function(){
32887 createTool : function(parentEl, className){
32888 var btn = Roo.DomHelper.append(parentEl, {
32890 cls: "x-layout-tools-button",
32893 cls: "roo-layout-tools-button-inner " + className,
32897 btn.addClassOnOver("roo-layout-tools-button-over");
32902 * Ext JS Library 1.1.1
32903 * Copyright(c) 2006-2007, Ext JS, LLC.
32905 * Originally Released Under LGPL - original licence link has changed is not relivant.
32908 * <script type="text/javascript">
32914 * @class Roo.SplitLayoutRegion
32915 * @extends Roo.LayoutRegion
32916 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32918 Roo.bootstrap.layout.Split = function(config){
32919 this.cursor = config.cursor;
32920 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
32923 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
32925 splitTip : "Drag to resize.",
32926 collapsibleSplitTip : "Drag to resize. Double click to hide.",
32927 useSplitTips : false,
32929 applyConfig : function(config){
32930 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
32933 onRender : function(ctr,pos) {
32935 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
32936 if(!this.config.split){
32941 var splitEl = Roo.DomHelper.append(ctr.dom, {
32943 id: this.el.id + "-split",
32944 cls: "roo-layout-split roo-layout-split-"+this.position,
32947 /** The SplitBar for this region
32948 * @type Roo.SplitBar */
32949 // does not exist yet...
32950 Roo.log([this.position, this.orientation]);
32952 this.split = new Roo.bootstrap.SplitBar({
32953 dragElement : splitEl,
32954 resizingElement: this.el,
32955 orientation : this.orientation
32958 this.split.on("moved", this.onSplitMove, this);
32959 this.split.useShim = this.config.useShim === true;
32960 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32961 if(this.useSplitTips){
32962 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32964 //if(config.collapsible){
32965 // this.split.el.on("dblclick", this.collapse, this);
32968 if(typeof this.config.minSize != "undefined"){
32969 this.split.minSize = this.config.minSize;
32971 if(typeof this.config.maxSize != "undefined"){
32972 this.split.maxSize = this.config.maxSize;
32974 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
32975 this.hideSplitter();
32980 getHMaxSize : function(){
32981 var cmax = this.config.maxSize || 10000;
32982 var center = this.mgr.getRegion("center");
32983 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32986 getVMaxSize : function(){
32987 var cmax = this.config.maxSize || 10000;
32988 var center = this.mgr.getRegion("center");
32989 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32992 onSplitMove : function(split, newSize){
32993 this.fireEvent("resized", this, newSize);
32997 * Returns the {@link Roo.SplitBar} for this region.
32998 * @return {Roo.SplitBar}
33000 getSplitBar : function(){
33005 this.hideSplitter();
33006 Roo.bootstrap.layout.Split.superclass.hide.call(this);
33009 hideSplitter : function(){
33011 this.split.el.setLocation(-2000,-2000);
33012 this.split.el.hide();
33018 this.split.el.show();
33020 Roo.bootstrap.layout.Split.superclass.show.call(this);
33023 beforeSlide: function(){
33024 if(Roo.isGecko){// firefox overflow auto bug workaround
33025 this.bodyEl.clip();
33027 this.tabs.bodyEl.clip();
33029 if(this.activePanel){
33030 this.activePanel.getEl().clip();
33032 if(this.activePanel.beforeSlide){
33033 this.activePanel.beforeSlide();
33039 afterSlide : function(){
33040 if(Roo.isGecko){// firefox overflow auto bug workaround
33041 this.bodyEl.unclip();
33043 this.tabs.bodyEl.unclip();
33045 if(this.activePanel){
33046 this.activePanel.getEl().unclip();
33047 if(this.activePanel.afterSlide){
33048 this.activePanel.afterSlide();
33054 initAutoHide : function(){
33055 if(this.autoHide !== false){
33056 if(!this.autoHideHd){
33057 var st = new Roo.util.DelayedTask(this.slideIn, this);
33058 this.autoHideHd = {
33059 "mouseout": function(e){
33060 if(!e.within(this.el, true)){
33064 "mouseover" : function(e){
33070 this.el.on(this.autoHideHd);
33074 clearAutoHide : function(){
33075 if(this.autoHide !== false){
33076 this.el.un("mouseout", this.autoHideHd.mouseout);
33077 this.el.un("mouseover", this.autoHideHd.mouseover);
33081 clearMonitor : function(){
33082 Roo.get(document).un("click", this.slideInIf, this);
33085 // these names are backwards but not changed for compat
33086 slideOut : function(){
33087 if(this.isSlid || this.el.hasActiveFx()){
33090 this.isSlid = true;
33091 if(this.collapseBtn){
33092 this.collapseBtn.hide();
33094 this.closeBtnState = this.closeBtn.getStyle('display');
33095 this.closeBtn.hide();
33097 this.stickBtn.show();
33100 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33101 this.beforeSlide();
33102 this.el.setStyle("z-index", 10001);
33103 this.el.slideIn(this.getSlideAnchor(), {
33104 callback: function(){
33106 this.initAutoHide();
33107 Roo.get(document).on("click", this.slideInIf, this);
33108 this.fireEvent("slideshow", this);
33115 afterSlideIn : function(){
33116 this.clearAutoHide();
33117 this.isSlid = false;
33118 this.clearMonitor();
33119 this.el.setStyle("z-index", "");
33120 if(this.collapseBtn){
33121 this.collapseBtn.show();
33123 this.closeBtn.setStyle('display', this.closeBtnState);
33125 this.stickBtn.hide();
33127 this.fireEvent("slidehide", this);
33130 slideIn : function(cb){
33131 if(!this.isSlid || this.el.hasActiveFx()){
33135 this.isSlid = false;
33136 this.beforeSlide();
33137 this.el.slideOut(this.getSlideAnchor(), {
33138 callback: function(){
33139 this.el.setLeftTop(-10000, -10000);
33141 this.afterSlideIn();
33149 slideInIf : function(e){
33150 if(!e.within(this.el)){
33155 animateCollapse : function(){
33156 this.beforeSlide();
33157 this.el.setStyle("z-index", 20000);
33158 var anchor = this.getSlideAnchor();
33159 this.el.slideOut(anchor, {
33160 callback : function(){
33161 this.el.setStyle("z-index", "");
33162 this.collapsedEl.slideIn(anchor, {duration:.3});
33164 this.el.setLocation(-10000,-10000);
33166 this.fireEvent("collapsed", this);
33173 animateExpand : function(){
33174 this.beforeSlide();
33175 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33176 this.el.setStyle("z-index", 20000);
33177 this.collapsedEl.hide({
33180 this.el.slideIn(this.getSlideAnchor(), {
33181 callback : function(){
33182 this.el.setStyle("z-index", "");
33185 this.split.el.show();
33187 this.fireEvent("invalidated", this);
33188 this.fireEvent("expanded", this);
33216 getAnchor : function(){
33217 return this.anchors[this.position];
33220 getCollapseAnchor : function(){
33221 return this.canchors[this.position];
33224 getSlideAnchor : function(){
33225 return this.sanchors[this.position];
33228 getAlignAdj : function(){
33229 var cm = this.cmargins;
33230 switch(this.position){
33246 getExpandAdj : function(){
33247 var c = this.collapsedEl, cm = this.cmargins;
33248 switch(this.position){
33250 return [-(cm.right+c.getWidth()+cm.left), 0];
33253 return [cm.right+c.getWidth()+cm.left, 0];
33256 return [0, -(cm.top+cm.bottom+c.getHeight())];
33259 return [0, cm.top+cm.bottom+c.getHeight()];
33265 * Ext JS Library 1.1.1
33266 * Copyright(c) 2006-2007, Ext JS, LLC.
33268 * Originally Released Under LGPL - original licence link has changed is not relivant.
33271 * <script type="text/javascript">
33274 * These classes are private internal classes
33276 Roo.bootstrap.layout.Center = function(config){
33277 config.region = "center";
33278 Roo.bootstrap.layout.Region.call(this, config);
33279 this.visible = true;
33280 this.minWidth = config.minWidth || 20;
33281 this.minHeight = config.minHeight || 20;
33284 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33286 // center panel can't be hidden
33290 // center panel can't be hidden
33293 getMinWidth: function(){
33294 return this.minWidth;
33297 getMinHeight: function(){
33298 return this.minHeight;
33311 Roo.bootstrap.layout.North = function(config)
33313 config.region = 'north';
33314 config.cursor = 'n-resize';
33316 Roo.bootstrap.layout.Split.call(this, config);
33320 this.split.placement = Roo.bootstrap.SplitBar.TOP;
33321 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33322 this.split.el.addClass("roo-layout-split-v");
33324 var size = config.initialSize || config.height;
33325 if(typeof size != "undefined"){
33326 this.el.setHeight(size);
33329 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33331 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33335 getBox : function(){
33336 if(this.collapsed){
33337 return this.collapsedEl.getBox();
33339 var box = this.el.getBox();
33341 box.height += this.split.el.getHeight();
33346 updateBox : function(box){
33347 if(this.split && !this.collapsed){
33348 box.height -= this.split.el.getHeight();
33349 this.split.el.setLeft(box.x);
33350 this.split.el.setTop(box.y+box.height);
33351 this.split.el.setWidth(box.width);
33353 if(this.collapsed){
33354 this.updateBody(box.width, null);
33356 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33364 Roo.bootstrap.layout.South = function(config){
33365 config.region = 'south';
33366 config.cursor = 's-resize';
33367 Roo.bootstrap.layout.Split.call(this, config);
33369 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33370 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33371 this.split.el.addClass("roo-layout-split-v");
33373 var size = config.initialSize || config.height;
33374 if(typeof size != "undefined"){
33375 this.el.setHeight(size);
33379 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33380 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33381 getBox : function(){
33382 if(this.collapsed){
33383 return this.collapsedEl.getBox();
33385 var box = this.el.getBox();
33387 var sh = this.split.el.getHeight();
33394 updateBox : function(box){
33395 if(this.split && !this.collapsed){
33396 var sh = this.split.el.getHeight();
33399 this.split.el.setLeft(box.x);
33400 this.split.el.setTop(box.y-sh);
33401 this.split.el.setWidth(box.width);
33403 if(this.collapsed){
33404 this.updateBody(box.width, null);
33406 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33410 Roo.bootstrap.layout.East = function(config){
33411 config.region = "east";
33412 config.cursor = "e-resize";
33413 Roo.bootstrap.layout.Split.call(this, config);
33415 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33416 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33417 this.split.el.addClass("roo-layout-split-h");
33419 var size = config.initialSize || config.width;
33420 if(typeof size != "undefined"){
33421 this.el.setWidth(size);
33424 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33425 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33426 getBox : function(){
33427 if(this.collapsed){
33428 return this.collapsedEl.getBox();
33430 var box = this.el.getBox();
33432 var sw = this.split.el.getWidth();
33439 updateBox : function(box){
33440 if(this.split && !this.collapsed){
33441 var sw = this.split.el.getWidth();
33443 this.split.el.setLeft(box.x);
33444 this.split.el.setTop(box.y);
33445 this.split.el.setHeight(box.height);
33448 if(this.collapsed){
33449 this.updateBody(null, box.height);
33451 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33455 Roo.bootstrap.layout.West = function(config){
33456 config.region = "west";
33457 config.cursor = "w-resize";
33459 Roo.bootstrap.layout.Split.call(this, config);
33461 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33462 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33463 this.split.el.addClass("roo-layout-split-h");
33467 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33468 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33470 onRender: function(ctr, pos)
33472 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33473 var size = this.config.initialSize || this.config.width;
33474 if(typeof size != "undefined"){
33475 this.el.setWidth(size);
33479 getBox : function(){
33480 if(this.collapsed){
33481 return this.collapsedEl.getBox();
33483 var box = this.el.getBox();
33485 box.width += this.split.el.getWidth();
33490 updateBox : function(box){
33491 if(this.split && !this.collapsed){
33492 var sw = this.split.el.getWidth();
33494 this.split.el.setLeft(box.x+box.width);
33495 this.split.el.setTop(box.y);
33496 this.split.el.setHeight(box.height);
33498 if(this.collapsed){
33499 this.updateBody(null, box.height);
33501 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33504 Roo.namespace("Roo.bootstrap.panel");/*
33506 * Ext JS Library 1.1.1
33507 * Copyright(c) 2006-2007, Ext JS, LLC.
33509 * Originally Released Under LGPL - original licence link has changed is not relivant.
33512 * <script type="text/javascript">
33515 * @class Roo.ContentPanel
33516 * @extends Roo.util.Observable
33517 * A basic ContentPanel element.
33518 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
33519 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
33520 * @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
33521 * @cfg {Boolean} closable True if the panel can be closed/removed
33522 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
33523 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33524 * @cfg {Toolbar} toolbar A toolbar for this panel
33525 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
33526 * @cfg {String} title The title for this panel
33527 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33528 * @cfg {String} url Calls {@link #setUrl} with this value
33529 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33530 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
33531 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
33532 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
33535 * Create a new ContentPanel.
33536 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33537 * @param {String/Object} config A string to set only the title or a config object
33538 * @param {String} content (optional) Set the HTML content for this panel
33539 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33541 Roo.bootstrap.panel.Content = function( config){
33543 var el = config.el;
33544 var content = config.content;
33546 if(config.autoCreate){ // xtype is available if this is called from factory
33549 this.el = Roo.get(el);
33550 if(!this.el && config && config.autoCreate){
33551 if(typeof config.autoCreate == "object"){
33552 if(!config.autoCreate.id){
33553 config.autoCreate.id = config.id||el;
33555 this.el = Roo.DomHelper.append(document.body,
33556 config.autoCreate, true);
33558 var elcfg = { tag: "div",
33559 cls: "roo-layout-inactive-content",
33563 elcfg.html = config.html;
33567 this.el = Roo.DomHelper.append(document.body, elcfg , true);
33570 this.closable = false;
33571 this.loaded = false;
33572 this.active = false;
33575 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33577 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33579 this.wrapEl = this.el.wrap();
33581 if (config.toolbar.items) {
33582 ti = config.toolbar.items ;
33583 delete config.toolbar.items ;
33587 this.toolbar.render(this.wrapEl, 'before');
33588 for(var i =0;i < ti.length;i++) {
33589 // Roo.log(['add child', items[i]]);
33590 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33592 this.toolbar.items = nitems;
33593 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33594 delete config.toolbar;
33598 // xtype created footer. - not sure if will work as we normally have to render first..
33599 if (this.footer && !this.footer.el && this.footer.xtype) {
33600 if (!this.wrapEl) {
33601 this.wrapEl = this.el.wrap();
33604 this.footer.container = this.wrapEl.createChild();
33606 this.footer = Roo.factory(this.footer, Roo);
33611 if(typeof config == "string"){
33612 this.title = config;
33614 Roo.apply(this, config);
33618 this.resizeEl = Roo.get(this.resizeEl, true);
33620 this.resizeEl = this.el;
33622 // handle view.xtype
33630 * Fires when this panel is activated.
33631 * @param {Roo.ContentPanel} this
33635 * @event deactivate
33636 * Fires when this panel is activated.
33637 * @param {Roo.ContentPanel} this
33639 "deactivate" : true,
33643 * Fires when this panel is resized if fitToFrame is true.
33644 * @param {Roo.ContentPanel} this
33645 * @param {Number} width The width after any component adjustments
33646 * @param {Number} height The height after any component adjustments
33652 * Fires when this tab is created
33653 * @param {Roo.ContentPanel} this
33664 if(this.autoScroll){
33665 this.resizeEl.setStyle("overflow", "auto");
33667 // fix randome scrolling
33668 //this.el.on('scroll', function() {
33669 // Roo.log('fix random scolling');
33670 // this.scrollTo('top',0);
33673 content = content || this.content;
33675 this.setContent(content);
33677 if(config && config.url){
33678 this.setUrl(this.url, this.params, this.loadOnce);
33683 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33685 if (this.view && typeof(this.view.xtype) != 'undefined') {
33686 this.view.el = this.el.appendChild(document.createElement("div"));
33687 this.view = Roo.factory(this.view);
33688 this.view.render && this.view.render(false, '');
33692 this.fireEvent('render', this);
33695 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33697 setRegion : function(region){
33698 this.region = region;
33700 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33702 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33707 * Returns the toolbar for this Panel if one was configured.
33708 * @return {Roo.Toolbar}
33710 getToolbar : function(){
33711 return this.toolbar;
33714 setActiveState : function(active){
33715 this.active = active;
33717 this.fireEvent("deactivate", this);
33719 this.fireEvent("activate", this);
33723 * Updates this panel's element
33724 * @param {String} content The new content
33725 * @param {Boolean} loadScripts (optional) true to look for and process scripts
33727 setContent : function(content, loadScripts){
33728 this.el.update(content, loadScripts);
33731 ignoreResize : function(w, h){
33732 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33735 this.lastSize = {width: w, height: h};
33740 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33741 * @return {Roo.UpdateManager} The UpdateManager
33743 getUpdateManager : function(){
33744 return this.el.getUpdateManager();
33747 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33748 * @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:
33751 url: "your-url.php",
33752 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33753 callback: yourFunction,
33754 scope: yourObject, //(optional scope)
33757 text: "Loading...",
33762 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33763 * 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.
33764 * @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}
33765 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33766 * @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.
33767 * @return {Roo.ContentPanel} this
33770 var um = this.el.getUpdateManager();
33771 um.update.apply(um, arguments);
33777 * 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.
33778 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33779 * @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)
33780 * @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)
33781 * @return {Roo.UpdateManager} The UpdateManager
33783 setUrl : function(url, params, loadOnce){
33784 if(this.refreshDelegate){
33785 this.removeListener("activate", this.refreshDelegate);
33787 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33788 this.on("activate", this.refreshDelegate);
33789 return this.el.getUpdateManager();
33792 _handleRefresh : function(url, params, loadOnce){
33793 if(!loadOnce || !this.loaded){
33794 var updater = this.el.getUpdateManager();
33795 updater.update(url, params, this._setLoaded.createDelegate(this));
33799 _setLoaded : function(){
33800 this.loaded = true;
33804 * Returns this panel's id
33807 getId : function(){
33812 * Returns this panel's element - used by regiosn to add.
33813 * @return {Roo.Element}
33815 getEl : function(){
33816 return this.wrapEl || this.el;
33821 adjustForComponents : function(width, height)
33823 //Roo.log('adjustForComponents ');
33824 if(this.resizeEl != this.el){
33825 width -= this.el.getFrameWidth('lr');
33826 height -= this.el.getFrameWidth('tb');
33829 var te = this.toolbar.getEl();
33830 height -= te.getHeight();
33831 te.setWidth(width);
33834 var te = this.footer.getEl();
33835 Roo.log("footer:" + te.getHeight());
33837 height -= te.getHeight();
33838 te.setWidth(width);
33842 if(this.adjustments){
33843 width += this.adjustments[0];
33844 height += this.adjustments[1];
33846 return {"width": width, "height": height};
33849 setSize : function(width, height){
33850 if(this.fitToFrame && !this.ignoreResize(width, height)){
33851 if(this.fitContainer && this.resizeEl != this.el){
33852 this.el.setSize(width, height);
33854 var size = this.adjustForComponents(width, height);
33855 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33856 this.fireEvent('resize', this, size.width, size.height);
33861 * Returns this panel's title
33864 getTitle : function(){
33869 * Set this panel's title
33870 * @param {String} title
33872 setTitle : function(title){
33873 this.title = title;
33875 this.region.updatePanelTitle(this, title);
33880 * Returns true is this panel was configured to be closable
33881 * @return {Boolean}
33883 isClosable : function(){
33884 return this.closable;
33887 beforeSlide : function(){
33889 this.resizeEl.clip();
33892 afterSlide : function(){
33894 this.resizeEl.unclip();
33898 * Force a content refresh from the URL specified in the {@link #setUrl} method.
33899 * Will fail silently if the {@link #setUrl} method has not been called.
33900 * This does not activate the panel, just updates its content.
33902 refresh : function(){
33903 if(this.refreshDelegate){
33904 this.loaded = false;
33905 this.refreshDelegate();
33910 * Destroys this panel
33912 destroy : function(){
33913 this.el.removeAllListeners();
33914 var tempEl = document.createElement("span");
33915 tempEl.appendChild(this.el.dom);
33916 tempEl.innerHTML = "";
33922 * form - if the content panel contains a form - this is a reference to it.
33923 * @type {Roo.form.Form}
33927 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33928 * This contains a reference to it.
33934 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33944 * @param {Object} cfg Xtype definition of item to add.
33948 getChildContainer: function () {
33949 return this.getEl();
33954 var ret = new Roo.factory(cfg);
33959 if (cfg.xtype.match(/^Form$/)) {
33962 //if (this.footer) {
33963 // el = this.footer.container.insertSibling(false, 'before');
33965 el = this.el.createChild();
33968 this.form = new Roo.form.Form(cfg);
33971 if ( this.form.allItems.length) {
33972 this.form.render(el.dom);
33976 // should only have one of theses..
33977 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33978 // views.. should not be just added - used named prop 'view''
33980 cfg.el = this.el.appendChild(document.createElement("div"));
33983 var ret = new Roo.factory(cfg);
33985 ret.render && ret.render(false, ''); // render blank..
33995 * @class Roo.bootstrap.panel.Grid
33996 * @extends Roo.bootstrap.panel.Content
33998 * Create a new GridPanel.
33999 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34000 * @param {Object} config A the config object
34006 Roo.bootstrap.panel.Grid = function(config)
34010 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34011 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34013 config.el = this.wrapper;
34014 //this.el = this.wrapper;
34016 if (config.container) {
34017 // ctor'ed from a Border/panel.grid
34020 this.wrapper.setStyle("overflow", "hidden");
34021 this.wrapper.addClass('roo-grid-container');
34026 if(config.toolbar){
34027 var tool_el = this.wrapper.createChild();
34028 this.toolbar = Roo.factory(config.toolbar);
34030 if (config.toolbar.items) {
34031 ti = config.toolbar.items ;
34032 delete config.toolbar.items ;
34036 this.toolbar.render(tool_el);
34037 for(var i =0;i < ti.length;i++) {
34038 // Roo.log(['add child', items[i]]);
34039 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34041 this.toolbar.items = nitems;
34043 delete config.toolbar;
34046 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34047 config.grid.scrollBody = true;;
34048 config.grid.monitorWindowResize = false; // turn off autosizing
34049 config.grid.autoHeight = false;
34050 config.grid.autoWidth = false;
34052 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34054 if (config.background) {
34055 // render grid on panel activation (if panel background)
34056 this.on('activate', function(gp) {
34057 if (!gp.grid.rendered) {
34058 gp.grid.render(el);
34059 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34065 this.grid.render(this.wrapper);
34066 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34069 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34070 // ??? needed ??? config.el = this.wrapper;
34075 // xtype created footer. - not sure if will work as we normally have to render first..
34076 if (this.footer && !this.footer.el && this.footer.xtype) {
34078 var ctr = this.grid.getView().getFooterPanel(true);
34079 this.footer.dataSource = this.grid.dataSource;
34080 this.footer = Roo.factory(this.footer, Roo);
34081 this.footer.render(ctr);
34091 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34092 getId : function(){
34093 return this.grid.id;
34097 * Returns the grid for this panel
34098 * @return {Roo.bootstrap.Table}
34100 getGrid : function(){
34104 setSize : function(width, height){
34105 if(!this.ignoreResize(width, height)){
34106 var grid = this.grid;
34107 var size = this.adjustForComponents(width, height);
34108 var gridel = grid.getGridEl();
34109 gridel.setSize(size.width, size.height);
34111 var thd = grid.getGridEl().select('thead',true).first();
34112 var tbd = grid.getGridEl().select('tbody', true).first();
34114 tbd.setSize(width, height - thd.getHeight());
34123 beforeSlide : function(){
34124 this.grid.getView().scroller.clip();
34127 afterSlide : function(){
34128 this.grid.getView().scroller.unclip();
34131 destroy : function(){
34132 this.grid.destroy();
34134 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
34139 * @class Roo.bootstrap.panel.Nest
34140 * @extends Roo.bootstrap.panel.Content
34142 * Create a new Panel, that can contain a layout.Border.
34145 * @param {Roo.BorderLayout} layout The layout for this panel
34146 * @param {String/Object} config A string to set only the title or a config object
34148 Roo.bootstrap.panel.Nest = function(config)
34150 // construct with only one argument..
34151 /* FIXME - implement nicer consturctors
34152 if (layout.layout) {
34154 layout = config.layout;
34155 delete config.layout;
34157 if (layout.xtype && !layout.getEl) {
34158 // then layout needs constructing..
34159 layout = Roo.factory(layout, Roo);
34163 config.el = config.layout.getEl();
34165 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34167 config.layout.monitorWindowResize = false; // turn off autosizing
34168 this.layout = config.layout;
34169 this.layout.getEl().addClass("roo-layout-nested-layout");
34176 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34178 setSize : function(width, height){
34179 if(!this.ignoreResize(width, height)){
34180 var size = this.adjustForComponents(width, height);
34181 var el = this.layout.getEl();
34182 el.setSize(size.width, size.height);
34183 var touch = el.dom.offsetWidth;
34184 this.layout.layout();
34185 // ie requires a double layout on the first pass
34186 if(Roo.isIE && !this.initialized){
34187 this.initialized = true;
34188 this.layout.layout();
34193 // activate all subpanels if not currently active..
34195 setActiveState : function(active){
34196 this.active = active;
34198 this.fireEvent("deactivate", this);
34202 this.fireEvent("activate", this);
34203 // not sure if this should happen before or after..
34204 if (!this.layout) {
34205 return; // should not happen..
34208 for (var r in this.layout.regions) {
34209 reg = this.layout.getRegion(r);
34210 if (reg.getActivePanel()) {
34211 //reg.showPanel(reg.getActivePanel()); // force it to activate..
34212 reg.setActivePanel(reg.getActivePanel());
34215 if (!reg.panels.length) {
34218 reg.showPanel(reg.getPanel(0));
34227 * Returns the nested BorderLayout for this panel
34228 * @return {Roo.BorderLayout}
34230 getLayout : function(){
34231 return this.layout;
34235 * Adds a xtype elements to the layout of the nested panel
34239 xtype : 'ContentPanel',
34246 xtype : 'NestedLayoutPanel',
34252 items : [ ... list of content panels or nested layout panels.. ]
34256 * @param {Object} cfg Xtype definition of item to add.
34258 addxtype : function(cfg) {
34259 return this.layout.addxtype(cfg);
34264 * Ext JS Library 1.1.1
34265 * Copyright(c) 2006-2007, Ext JS, LLC.
34267 * Originally Released Under LGPL - original licence link has changed is not relivant.
34270 * <script type="text/javascript">
34273 * @class Roo.TabPanel
34274 * @extends Roo.util.Observable
34275 * A lightweight tab container.
34279 // basic tabs 1, built from existing content
34280 var tabs = new Roo.TabPanel("tabs1");
34281 tabs.addTab("script", "View Script");
34282 tabs.addTab("markup", "View Markup");
34283 tabs.activate("script");
34285 // more advanced tabs, built from javascript
34286 var jtabs = new Roo.TabPanel("jtabs");
34287 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34289 // set up the UpdateManager
34290 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34291 var updater = tab2.getUpdateManager();
34292 updater.setDefaultUrl("ajax1.htm");
34293 tab2.on('activate', updater.refresh, updater, true);
34295 // Use setUrl for Ajax loading
34296 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34297 tab3.setUrl("ajax2.htm", null, true);
34300 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34303 jtabs.activate("jtabs-1");
34306 * Create a new TabPanel.
34307 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34308 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34310 Roo.bootstrap.panel.Tabs = function(config){
34312 * The container element for this TabPanel.
34313 * @type Roo.Element
34315 this.el = Roo.get(config.el);
34318 if(typeof config == "boolean"){
34319 this.tabPosition = config ? "bottom" : "top";
34321 Roo.apply(this, config);
34325 if(this.tabPosition == "bottom"){
34326 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34327 this.el.addClass("roo-tabs-bottom");
34329 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34330 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34331 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34333 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34335 if(this.tabPosition != "bottom"){
34336 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34337 * @type Roo.Element
34339 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34340 this.el.addClass("roo-tabs-top");
34344 this.bodyEl.setStyle("position", "relative");
34346 this.active = null;
34347 this.activateDelegate = this.activate.createDelegate(this);
34352 * Fires when the active tab changes
34353 * @param {Roo.TabPanel} this
34354 * @param {Roo.TabPanelItem} activePanel The new active tab
34358 * @event beforetabchange
34359 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34360 * @param {Roo.TabPanel} this
34361 * @param {Object} e Set cancel to true on this object to cancel the tab change
34362 * @param {Roo.TabPanelItem} tab The tab being changed to
34364 "beforetabchange" : true
34367 Roo.EventManager.onWindowResize(this.onResize, this);
34368 this.cpad = this.el.getPadding("lr");
34369 this.hiddenCount = 0;
34372 // toolbar on the tabbar support...
34373 if (this.toolbar) {
34374 alert("no toolbar support yet");
34375 this.toolbar = false;
34377 var tcfg = this.toolbar;
34378 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
34379 this.toolbar = new Roo.Toolbar(tcfg);
34380 if (Roo.isSafari) {
34381 var tbl = tcfg.container.child('table', true);
34382 tbl.setAttribute('width', '100%');
34390 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34393 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34395 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34397 tabPosition : "top",
34399 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34401 currentTabWidth : 0,
34403 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34407 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34411 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34413 preferredTabWidth : 175,
34415 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34417 resizeTabs : false,
34419 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34421 monitorResize : true,
34423 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
34428 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34429 * @param {String} id The id of the div to use <b>or create</b>
34430 * @param {String} text The text for the tab
34431 * @param {String} content (optional) Content to put in the TabPanelItem body
34432 * @param {Boolean} closable (optional) True to create a close icon on the tab
34433 * @return {Roo.TabPanelItem} The created TabPanelItem
34435 addTab : function(id, text, content, closable)
34437 var item = new Roo.bootstrap.panel.TabItem({
34441 closable : closable
34443 this.addTabItem(item);
34445 item.setContent(content);
34451 * Returns the {@link Roo.TabPanelItem} with the specified id/index
34452 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34453 * @return {Roo.TabPanelItem}
34455 getTab : function(id){
34456 return this.items[id];
34460 * Hides the {@link Roo.TabPanelItem} with the specified id/index
34461 * @param {String/Number} id The id or index of the TabPanelItem to hide.
34463 hideTab : function(id){
34464 var t = this.items[id];
34467 this.hiddenCount++;
34468 this.autoSizeTabs();
34473 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34474 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34476 unhideTab : function(id){
34477 var t = this.items[id];
34479 t.setHidden(false);
34480 this.hiddenCount--;
34481 this.autoSizeTabs();
34486 * Adds an existing {@link Roo.TabPanelItem}.
34487 * @param {Roo.TabPanelItem} item The TabPanelItem to add
34489 addTabItem : function(item){
34490 this.items[item.id] = item;
34491 this.items.push(item);
34492 // if(this.resizeTabs){
34493 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34494 // this.autoSizeTabs();
34496 // item.autoSize();
34501 * Removes a {@link Roo.TabPanelItem}.
34502 * @param {String/Number} id The id or index of the TabPanelItem to remove.
34504 removeTab : function(id){
34505 var items = this.items;
34506 var tab = items[id];
34507 if(!tab) { return; }
34508 var index = items.indexOf(tab);
34509 if(this.active == tab && items.length > 1){
34510 var newTab = this.getNextAvailable(index);
34515 this.stripEl.dom.removeChild(tab.pnode.dom);
34516 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34517 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34519 items.splice(index, 1);
34520 delete this.items[tab.id];
34521 tab.fireEvent("close", tab);
34522 tab.purgeListeners();
34523 this.autoSizeTabs();
34526 getNextAvailable : function(start){
34527 var items = this.items;
34529 // look for a next tab that will slide over to
34530 // replace the one being removed
34531 while(index < items.length){
34532 var item = items[++index];
34533 if(item && !item.isHidden()){
34537 // if one isn't found select the previous tab (on the left)
34540 var item = items[--index];
34541 if(item && !item.isHidden()){
34549 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34550 * @param {String/Number} id The id or index of the TabPanelItem to disable.
34552 disableTab : function(id){
34553 var tab = this.items[id];
34554 if(tab && this.active != tab){
34560 * Enables a {@link Roo.TabPanelItem} that is disabled.
34561 * @param {String/Number} id The id or index of the TabPanelItem to enable.
34563 enableTab : function(id){
34564 var tab = this.items[id];
34569 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34570 * @param {String/Number} id The id or index of the TabPanelItem to activate.
34571 * @return {Roo.TabPanelItem} The TabPanelItem.
34573 activate : function(id){
34574 var tab = this.items[id];
34578 if(tab == this.active || tab.disabled){
34582 this.fireEvent("beforetabchange", this, e, tab);
34583 if(e.cancel !== true && !tab.disabled){
34585 this.active.hide();
34587 this.active = this.items[id];
34588 this.active.show();
34589 this.fireEvent("tabchange", this, this.active);
34595 * Gets the active {@link Roo.TabPanelItem}.
34596 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34598 getActiveTab : function(){
34599 return this.active;
34603 * Updates the tab body element to fit the height of the container element
34604 * for overflow scrolling
34605 * @param {Number} targetHeight (optional) Override the starting height from the elements height
34607 syncHeight : function(targetHeight){
34608 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34609 var bm = this.bodyEl.getMargins();
34610 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34611 this.bodyEl.setHeight(newHeight);
34615 onResize : function(){
34616 if(this.monitorResize){
34617 this.autoSizeTabs();
34622 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34624 beginUpdate : function(){
34625 this.updating = true;
34629 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34631 endUpdate : function(){
34632 this.updating = false;
34633 this.autoSizeTabs();
34637 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34639 autoSizeTabs : function(){
34640 var count = this.items.length;
34641 var vcount = count - this.hiddenCount;
34642 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34645 var w = Math.max(this.el.getWidth() - this.cpad, 10);
34646 var availWidth = Math.floor(w / vcount);
34647 var b = this.stripBody;
34648 if(b.getWidth() > w){
34649 var tabs = this.items;
34650 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34651 if(availWidth < this.minTabWidth){
34652 /*if(!this.sleft){ // incomplete scrolling code
34653 this.createScrollButtons();
34656 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34659 if(this.currentTabWidth < this.preferredTabWidth){
34660 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34666 * Returns the number of tabs in this TabPanel.
34669 getCount : function(){
34670 return this.items.length;
34674 * Resizes all the tabs to the passed width
34675 * @param {Number} The new width
34677 setTabWidth : function(width){
34678 this.currentTabWidth = width;
34679 for(var i = 0, len = this.items.length; i < len; i++) {
34680 if(!this.items[i].isHidden()) {
34681 this.items[i].setWidth(width);
34687 * Destroys this TabPanel
34688 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34690 destroy : function(removeEl){
34691 Roo.EventManager.removeResizeListener(this.onResize, this);
34692 for(var i = 0, len = this.items.length; i < len; i++){
34693 this.items[i].purgeListeners();
34695 if(removeEl === true){
34696 this.el.update("");
34701 createStrip : function(container)
34703 var strip = document.createElement("nav");
34704 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34705 container.appendChild(strip);
34709 createStripList : function(strip)
34711 // div wrapper for retard IE
34712 // returns the "tr" element.
34713 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34714 //'<div class="x-tabs-strip-wrap">'+
34715 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34716 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34717 return strip.firstChild; //.firstChild.firstChild.firstChild;
34719 createBody : function(container)
34721 var body = document.createElement("div");
34722 Roo.id(body, "tab-body");
34723 //Roo.fly(body).addClass("x-tabs-body");
34724 Roo.fly(body).addClass("tab-content");
34725 container.appendChild(body);
34728 createItemBody :function(bodyEl, id){
34729 var body = Roo.getDom(id);
34731 body = document.createElement("div");
34734 //Roo.fly(body).addClass("x-tabs-item-body");
34735 Roo.fly(body).addClass("tab-pane");
34736 bodyEl.insertBefore(body, bodyEl.firstChild);
34740 createStripElements : function(stripEl, text, closable)
34742 var td = document.createElement("li"); // was td..
34743 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34744 //stripEl.appendChild(td);
34746 td.className = "x-tabs-closable";
34747 if(!this.closeTpl){
34748 this.closeTpl = new Roo.Template(
34749 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34750 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34751 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
34754 var el = this.closeTpl.overwrite(td, {"text": text});
34755 var close = el.getElementsByTagName("div")[0];
34756 var inner = el.getElementsByTagName("em")[0];
34757 return {"el": el, "close": close, "inner": inner};
34760 // not sure what this is..
34762 //this.tabTpl = new Roo.Template(
34763 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34764 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34766 this.tabTpl = new Roo.Template(
34768 '<span unselectable="on"' +
34769 (this.disableTooltips ? '' : ' title="{text}"') +
34770 ' >{text}</span></span></a>'
34774 var el = this.tabTpl.overwrite(td, {"text": text});
34775 var inner = el.getElementsByTagName("span")[0];
34776 return {"el": el, "inner": inner};
34784 * @class Roo.TabPanelItem
34785 * @extends Roo.util.Observable
34786 * Represents an individual item (tab plus body) in a TabPanel.
34787 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
34788 * @param {String} id The id of this TabPanelItem
34789 * @param {String} text The text for the tab of this TabPanelItem
34790 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
34792 Roo.bootstrap.panel.TabItem = function(config){
34794 * The {@link Roo.TabPanel} this TabPanelItem belongs to
34795 * @type Roo.TabPanel
34797 this.tabPanel = config.panel;
34799 * The id for this TabPanelItem
34802 this.id = config.id;
34804 this.disabled = false;
34806 this.text = config.text;
34808 this.loaded = false;
34809 this.closable = config.closable;
34812 * The body element for this TabPanelItem.
34813 * @type Roo.Element
34815 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
34816 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
34817 this.bodyEl.setStyle("display", "block");
34818 this.bodyEl.setStyle("zoom", "1");
34819 //this.hideAction();
34821 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
34823 this.el = Roo.get(els.el);
34824 this.inner = Roo.get(els.inner, true);
34825 this.textEl = Roo.get(this.el.dom.firstChild, true);
34826 this.pnode = Roo.get(els.el.parentNode, true);
34827 this.el.on("mousedown", this.onTabMouseDown, this);
34828 this.el.on("click", this.onTabClick, this);
34830 if(config.closable){
34831 var c = Roo.get(els.close, true);
34832 c.dom.title = this.closeText;
34833 c.addClassOnOver("close-over");
34834 c.on("click", this.closeClick, this);
34840 * Fires when this tab becomes the active tab.
34841 * @param {Roo.TabPanel} tabPanel The parent TabPanel
34842 * @param {Roo.TabPanelItem} this
34846 * @event beforeclose
34847 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
34848 * @param {Roo.TabPanelItem} this
34849 * @param {Object} e Set cancel to true on this object to cancel the close.
34851 "beforeclose": true,
34854 * Fires when this tab is closed.
34855 * @param {Roo.TabPanelItem} this
34859 * @event deactivate
34860 * Fires when this tab is no longer the active tab.
34861 * @param {Roo.TabPanel} tabPanel The parent TabPanel
34862 * @param {Roo.TabPanelItem} this
34864 "deactivate" : true
34866 this.hidden = false;
34868 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
34871 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
34873 purgeListeners : function(){
34874 Roo.util.Observable.prototype.purgeListeners.call(this);
34875 this.el.removeAllListeners();
34878 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
34881 this.pnode.addClass("active");
34884 this.tabPanel.stripWrap.repaint();
34886 this.fireEvent("activate", this.tabPanel, this);
34890 * Returns true if this tab is the active tab.
34891 * @return {Boolean}
34893 isActive : function(){
34894 return this.tabPanel.getActiveTab() == this;
34898 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
34901 this.pnode.removeClass("active");
34903 this.fireEvent("deactivate", this.tabPanel, this);
34906 hideAction : function(){
34907 this.bodyEl.hide();
34908 this.bodyEl.setStyle("position", "absolute");
34909 this.bodyEl.setLeft("-20000px");
34910 this.bodyEl.setTop("-20000px");
34913 showAction : function(){
34914 this.bodyEl.setStyle("position", "relative");
34915 this.bodyEl.setTop("");
34916 this.bodyEl.setLeft("");
34917 this.bodyEl.show();
34921 * Set the tooltip for the tab.
34922 * @param {String} tooltip The tab's tooltip
34924 setTooltip : function(text){
34925 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
34926 this.textEl.dom.qtip = text;
34927 this.textEl.dom.removeAttribute('title');
34929 this.textEl.dom.title = text;
34933 onTabClick : function(e){
34934 e.preventDefault();
34935 this.tabPanel.activate(this.id);
34938 onTabMouseDown : function(e){
34939 e.preventDefault();
34940 this.tabPanel.activate(this.id);
34943 getWidth : function(){
34944 return this.inner.getWidth();
34947 setWidth : function(width){
34948 var iwidth = width - this.pnode.getPadding("lr");
34949 this.inner.setWidth(iwidth);
34950 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
34951 this.pnode.setWidth(width);
34955 * Show or hide the tab
34956 * @param {Boolean} hidden True to hide or false to show.
34958 setHidden : function(hidden){
34959 this.hidden = hidden;
34960 this.pnode.setStyle("display", hidden ? "none" : "");
34964 * Returns true if this tab is "hidden"
34965 * @return {Boolean}
34967 isHidden : function(){
34968 return this.hidden;
34972 * Returns the text for this tab
34975 getText : function(){
34979 autoSize : function(){
34980 //this.el.beginMeasure();
34981 this.textEl.setWidth(1);
34983 * #2804 [new] Tabs in Roojs
34984 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
34986 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
34987 //this.el.endMeasure();
34991 * Sets the text for the tab (Note: this also sets the tooltip text)
34992 * @param {String} text The tab's text and tooltip
34994 setText : function(text){
34996 this.textEl.update(text);
34997 this.setTooltip(text);
34998 //if(!this.tabPanel.resizeTabs){
34999 // this.autoSize();
35003 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35005 activate : function(){
35006 this.tabPanel.activate(this.id);
35010 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35012 disable : function(){
35013 if(this.tabPanel.active != this){
35014 this.disabled = true;
35015 this.pnode.addClass("disabled");
35020 * Enables this TabPanelItem if it was previously disabled.
35022 enable : function(){
35023 this.disabled = false;
35024 this.pnode.removeClass("disabled");
35028 * Sets the content for this TabPanelItem.
35029 * @param {String} content The content
35030 * @param {Boolean} loadScripts true to look for and load scripts
35032 setContent : function(content, loadScripts){
35033 this.bodyEl.update(content, loadScripts);
35037 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35038 * @return {Roo.UpdateManager} The UpdateManager
35040 getUpdateManager : function(){
35041 return this.bodyEl.getUpdateManager();
35045 * Set a URL to be used to load the content for this TabPanelItem.
35046 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35047 * @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)
35048 * @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)
35049 * @return {Roo.UpdateManager} The UpdateManager
35051 setUrl : function(url, params, loadOnce){
35052 if(this.refreshDelegate){
35053 this.un('activate', this.refreshDelegate);
35055 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35056 this.on("activate", this.refreshDelegate);
35057 return this.bodyEl.getUpdateManager();
35061 _handleRefresh : function(url, params, loadOnce){
35062 if(!loadOnce || !this.loaded){
35063 var updater = this.bodyEl.getUpdateManager();
35064 updater.update(url, params, this._setLoaded.createDelegate(this));
35069 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
35070 * Will fail silently if the setUrl method has not been called.
35071 * This does not activate the panel, just updates its content.
35073 refresh : function(){
35074 if(this.refreshDelegate){
35075 this.loaded = false;
35076 this.refreshDelegate();
35081 _setLoaded : function(){
35082 this.loaded = true;
35086 closeClick : function(e){
35089 this.fireEvent("beforeclose", this, o);
35090 if(o.cancel !== true){
35091 this.tabPanel.removeTab(this.id);
35095 * The text displayed in the tooltip for the close icon.
35098 closeText : "Close this tab"