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);
1518 * Sets the url of the image - used to update it
1519 * @param {String} url the url of the image
1522 setSrc : function(url)
1525 this.el.select('img', true).first().dom.src = url;
1541 * @class Roo.bootstrap.Link
1542 * @extends Roo.bootstrap.Component
1543 * Bootstrap Link Class
1544 * @cfg {String} alt image alternative text
1545 * @cfg {String} href a tag href
1546 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1547 * @cfg {String} html the content of the link.
1548 * @cfg {String} anchor name for the anchor link
1549 * @cfg {String} fa - favicon
1551 * @cfg {Boolean} preventDefault (true | false) default false
1555 * Create a new Input
1556 * @param {Object} config The config object
1559 Roo.bootstrap.Link = function(config){
1560 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1566 * The img click event for the img.
1567 * @param {Roo.EventObject} e
1573 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1577 preventDefault: false,
1583 getAutoCreate : function()
1585 var html = this.html || '';
1587 if (this.fa !== false) {
1588 html = '<i class="fa fa-' + this.fa + '"></i>';
1593 // anchor's do not require html/href...
1594 if (this.anchor === false) {
1596 cfg.href = this.href || '#';
1598 cfg.name = this.anchor;
1599 if (this.html !== false || this.fa !== false) {
1602 if (this.href !== false) {
1603 cfg.href = this.href;
1607 if(this.alt !== false){
1612 if(this.target !== false) {
1613 cfg.target = this.target;
1619 initEvents: function() {
1621 if(!this.href || this.preventDefault){
1622 this.el.on('click', this.onClick, this);
1626 onClick : function(e)
1628 if(this.preventDefault){
1631 //Roo.log('img onclick');
1632 this.fireEvent('click', this, e);
1645 * @class Roo.bootstrap.Header
1646 * @extends Roo.bootstrap.Component
1647 * Bootstrap Header class
1648 * @cfg {String} html content of header
1649 * @cfg {Number} level (1|2|3|4|5|6) default 1
1652 * Create a new Header
1653 * @param {Object} config The config object
1657 Roo.bootstrap.Header = function(config){
1658 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1661 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1669 getAutoCreate : function(){
1674 tag: 'h' + (1 *this.level),
1675 html: this.html || ''
1687 * Ext JS Library 1.1.1
1688 * Copyright(c) 2006-2007, Ext JS, LLC.
1690 * Originally Released Under LGPL - original licence link has changed is not relivant.
1693 * <script type="text/javascript">
1697 * @class Roo.bootstrap.MenuMgr
1698 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1701 Roo.bootstrap.MenuMgr = function(){
1702 var menus, active, groups = {}, attached = false, lastShow = new Date();
1704 // private - called when first menu is created
1707 active = new Roo.util.MixedCollection();
1708 Roo.get(document).addKeyListener(27, function(){
1709 if(active.length > 0){
1717 if(active && active.length > 0){
1718 var c = active.clone();
1728 if(active.length < 1){
1729 Roo.get(document).un("mouseup", onMouseDown);
1737 var last = active.last();
1738 lastShow = new Date();
1741 Roo.get(document).on("mouseup", onMouseDown);
1746 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1747 m.parentMenu.activeChild = m;
1748 }else if(last && last.isVisible()){
1749 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1754 function onBeforeHide(m){
1756 m.activeChild.hide();
1758 if(m.autoHideTimer){
1759 clearTimeout(m.autoHideTimer);
1760 delete m.autoHideTimer;
1765 function onBeforeShow(m){
1766 var pm = m.parentMenu;
1767 if(!pm && !m.allowOtherMenus){
1769 }else if(pm && pm.activeChild && active != m){
1770 pm.activeChild.hide();
1774 // private this should really trigger on mouseup..
1775 function onMouseDown(e){
1776 Roo.log("on Mouse Up");
1778 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1779 Roo.log("MenuManager hideAll");
1788 function onBeforeCheck(mi, state){
1790 var g = groups[mi.group];
1791 for(var i = 0, l = g.length; i < l; i++){
1793 g[i].setChecked(false);
1802 * Hides all menus that are currently visible
1804 hideAll : function(){
1809 register : function(menu){
1813 menus[menu.id] = menu;
1814 menu.on("beforehide", onBeforeHide);
1815 menu.on("hide", onHide);
1816 menu.on("beforeshow", onBeforeShow);
1817 menu.on("show", onShow);
1819 if(g && menu.events["checkchange"]){
1823 groups[g].push(menu);
1824 menu.on("checkchange", onCheck);
1829 * Returns a {@link Roo.menu.Menu} object
1830 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1831 * be used to generate and return a new Menu instance.
1833 get : function(menu){
1834 if(typeof menu == "string"){ // menu id
1836 }else if(menu.events){ // menu instance
1839 /*else if(typeof menu.length == 'number'){ // array of menu items?
1840 return new Roo.bootstrap.Menu({items:menu});
1841 }else{ // otherwise, must be a config
1842 return new Roo.bootstrap.Menu(menu);
1849 unregister : function(menu){
1850 delete menus[menu.id];
1851 menu.un("beforehide", onBeforeHide);
1852 menu.un("hide", onHide);
1853 menu.un("beforeshow", onBeforeShow);
1854 menu.un("show", onShow);
1856 if(g && menu.events["checkchange"]){
1857 groups[g].remove(menu);
1858 menu.un("checkchange", onCheck);
1863 registerCheckable : function(menuItem){
1864 var g = menuItem.group;
1869 groups[g].push(menuItem);
1870 menuItem.on("beforecheckchange", onBeforeCheck);
1875 unregisterCheckable : function(menuItem){
1876 var g = menuItem.group;
1878 groups[g].remove(menuItem);
1879 menuItem.un("beforecheckchange", onBeforeCheck);
1891 * @class Roo.bootstrap.Menu
1892 * @extends Roo.bootstrap.Component
1893 * Bootstrap Menu class - container for MenuItems
1894 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1895 * @cfg {bool} hidden if the menu should be hidden when rendered.
1896 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1897 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1901 * @param {Object} config The config object
1905 Roo.bootstrap.Menu = function(config){
1906 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1907 if (this.registerMenu && this.type != 'treeview') {
1908 Roo.bootstrap.MenuMgr.register(this);
1913 * Fires before this menu is displayed
1914 * @param {Roo.menu.Menu} this
1919 * Fires before this menu is hidden
1920 * @param {Roo.menu.Menu} this
1925 * Fires after this menu is displayed
1926 * @param {Roo.menu.Menu} this
1931 * Fires after this menu is hidden
1932 * @param {Roo.menu.Menu} this
1937 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1938 * @param {Roo.menu.Menu} this
1939 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1940 * @param {Roo.EventObject} e
1945 * Fires when the mouse is hovering over this menu
1946 * @param {Roo.menu.Menu} this
1947 * @param {Roo.EventObject} e
1948 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1953 * Fires when the mouse exits this menu
1954 * @param {Roo.menu.Menu} this
1955 * @param {Roo.EventObject} e
1956 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1961 * Fires when a menu item contained in this menu is clicked
1962 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1963 * @param {Roo.EventObject} e
1967 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1970 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1974 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1977 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1979 registerMenu : true,
1981 menuItems :false, // stores the menu items..
1991 getChildContainer : function() {
1995 getAutoCreate : function(){
1997 //if (['right'].indexOf(this.align)!==-1) {
1998 // cfg.cn[1].cls += ' pull-right'
2004 cls : 'dropdown-menu' ,
2005 style : 'z-index:1000'
2009 if (this.type === 'submenu') {
2010 cfg.cls = 'submenu active';
2012 if (this.type === 'treeview') {
2013 cfg.cls = 'treeview-menu';
2018 initEvents : function() {
2020 // Roo.log("ADD event");
2021 // Roo.log(this.triggerEl.dom);
2023 this.triggerEl.on('click', this.onTriggerClick, this);
2025 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2027 this.triggerEl.addClass('dropdown-toggle');
2030 this.el.on('touchstart' , this.onTouch, this);
2032 this.el.on('click' , this.onClick, this);
2034 this.el.on("mouseover", this.onMouseOver, this);
2035 this.el.on("mouseout", this.onMouseOut, this);
2039 findTargetItem : function(e)
2041 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2045 //Roo.log(t); Roo.log(t.id);
2047 //Roo.log(this.menuitems);
2048 return this.menuitems.get(t.id);
2050 //return this.items.get(t.menuItemId);
2056 onTouch : function(e)
2058 Roo.log("menu.onTouch");
2059 //e.stopEvent(); this make the user popdown broken
2063 onClick : function(e)
2065 Roo.log("menu.onClick");
2067 var t = this.findTargetItem(e);
2068 if(!t || t.isContainer){
2073 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2074 if(t == this.activeItem && t.shouldDeactivate(e)){
2075 this.activeItem.deactivate();
2076 delete this.activeItem;
2080 this.setActiveItem(t, true);
2088 Roo.log('pass click event');
2092 this.fireEvent("click", this, t, e);
2096 (function() { _this.hide(); }).defer(500);
2099 onMouseOver : function(e){
2100 var t = this.findTargetItem(e);
2103 // if(t.canActivate && !t.disabled){
2104 // this.setActiveItem(t, true);
2108 this.fireEvent("mouseover", this, e, t);
2110 isVisible : function(){
2111 return !this.hidden;
2113 onMouseOut : function(e){
2114 var t = this.findTargetItem(e);
2117 // if(t == this.activeItem && t.shouldDeactivate(e)){
2118 // this.activeItem.deactivate();
2119 // delete this.activeItem;
2122 this.fireEvent("mouseout", this, e, t);
2127 * Displays this menu relative to another element
2128 * @param {String/HTMLElement/Roo.Element} element The element to align to
2129 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2130 * the element (defaults to this.defaultAlign)
2131 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2133 show : function(el, pos, parentMenu){
2134 this.parentMenu = parentMenu;
2138 this.fireEvent("beforeshow", this);
2139 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2142 * Displays this menu at a specific xy position
2143 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2144 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2146 showAt : function(xy, parentMenu, /* private: */_e){
2147 this.parentMenu = parentMenu;
2152 this.fireEvent("beforeshow", this);
2153 //xy = this.el.adjustForConstraints(xy);
2157 this.hideMenuItems();
2158 this.hidden = false;
2159 this.triggerEl.addClass('open');
2161 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2162 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2165 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2170 this.fireEvent("show", this);
2176 this.doFocus.defer(50, this);
2180 doFocus : function(){
2182 this.focusEl.focus();
2187 * Hides this menu and optionally all parent menus
2188 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2190 hide : function(deep)
2193 this.hideMenuItems();
2194 if(this.el && this.isVisible()){
2195 this.fireEvent("beforehide", this);
2196 if(this.activeItem){
2197 this.activeItem.deactivate();
2198 this.activeItem = null;
2200 this.triggerEl.removeClass('open');;
2202 this.fireEvent("hide", this);
2204 if(deep === true && this.parentMenu){
2205 this.parentMenu.hide(true);
2209 onTriggerClick : function(e)
2211 Roo.log('trigger click');
2213 var target = e.getTarget();
2215 Roo.log(target.nodeName.toLowerCase());
2217 if(target.nodeName.toLowerCase() === 'i'){
2223 onTriggerPress : function(e)
2225 Roo.log('trigger press');
2226 //Roo.log(e.getTarget());
2227 // Roo.log(this.triggerEl.dom);
2229 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2230 var pel = Roo.get(e.getTarget());
2231 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2232 Roo.log('is treeview or dropdown?');
2236 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2240 if (this.isVisible()) {
2245 this.show(this.triggerEl, false, false);
2248 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2255 hideMenuItems : function()
2257 Roo.log("hide Menu Items");
2261 //$(backdrop).remove()
2262 this.el.select('.open',true).each(function(aa) {
2264 aa.removeClass('open');
2265 //var parent = getParent($(this))
2266 //var relatedTarget = { relatedTarget: this }
2268 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2269 //if (e.isDefaultPrevented()) return
2270 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2273 addxtypeChild : function (tree, cntr) {
2274 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2276 this.menuitems.add(comp);
2297 * @class Roo.bootstrap.MenuItem
2298 * @extends Roo.bootstrap.Component
2299 * Bootstrap MenuItem class
2300 * @cfg {String} html the menu label
2301 * @cfg {String} href the link
2302 * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2303 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2304 * @cfg {Boolean} active used on sidebars to highlight active itesm
2305 * @cfg {String} fa favicon to show on left of menu item.
2306 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2310 * Create a new MenuItem
2311 * @param {Object} config The config object
2315 Roo.bootstrap.MenuItem = function(config){
2316 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2321 * The raw click event for the entire grid.
2322 * @param {Roo.bootstrap.MenuItem} this
2323 * @param {Roo.EventObject} e
2329 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2333 preventDefault: true,
2334 isContainer : false,
2338 getAutoCreate : function(){
2340 if(this.isContainer){
2343 cls: 'dropdown-menu-item'
2357 if (this.fa !== false) {
2360 cls : 'fa fa-' + this.fa
2369 cls: 'dropdown-menu-item',
2372 if (this.parent().type == 'treeview') {
2373 cfg.cls = 'treeview-menu';
2376 cfg.cls += ' active';
2381 anc.href = this.href || cfg.cn[0].href ;
2382 ctag.html = this.html || cfg.cn[0].html ;
2386 initEvents: function()
2388 if (this.parent().type == 'treeview') {
2389 this.el.select('a').on('click', this.onClick, this);
2392 this.menu.parentType = this.xtype;
2393 this.menu.triggerEl = this.el;
2394 this.menu = this.addxtype(Roo.apply({}, this.menu));
2398 onClick : function(e)
2400 Roo.log('item on click ');
2401 //if(this.preventDefault){
2402 // e.preventDefault();
2404 //this.parent().hideMenuItems();
2406 this.fireEvent('click', this, e);
2425 * @class Roo.bootstrap.MenuSeparator
2426 * @extends Roo.bootstrap.Component
2427 * Bootstrap MenuSeparator class
2430 * Create a new MenuItem
2431 * @param {Object} config The config object
2435 Roo.bootstrap.MenuSeparator = function(config){
2436 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2439 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2441 getAutoCreate : function(){
2460 * @class Roo.bootstrap.Modal
2461 * @extends Roo.bootstrap.Component
2462 * Bootstrap Modal class
2463 * @cfg {String} title Title of dialog
2464 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2465 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2466 * @cfg {Boolean} specificTitle default false
2467 * @cfg {Array} buttons Array of buttons or standard button set..
2468 * @cfg {String} buttonPosition (left|right|center) default right
2469 * @cfg {Boolean} animate default true
2470 * @cfg {Boolean} allow_close default true
2471 * @cfg {Boolean} fitwindow default false
2472 * @cfg {String} size (sm|lg) default empty
2476 * Create a new Modal Dialog
2477 * @param {Object} config The config object
2480 Roo.bootstrap.Modal = function(config){
2481 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2486 * The raw btnclick event for the button
2487 * @param {Roo.EventObject} e
2491 this.buttons = this.buttons || [];
2494 this.tmpl = Roo.factory(this.tmpl);
2499 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2501 title : 'test dialog',
2511 specificTitle: false,
2513 buttonPosition: 'right',
2532 onRender : function(ct, position)
2534 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2537 var cfg = Roo.apply({}, this.getAutoCreate());
2540 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2542 //if (!cfg.name.length) {
2546 cfg.cls += ' ' + this.cls;
2549 cfg.style = this.style;
2551 this.el = Roo.get(document.body).createChild(cfg, position);
2553 //var type = this.el.dom.type;
2556 if(this.tabIndex !== undefined){
2557 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2560 this.dialogEl = this.el.select('.modal-dialog',true).first();
2561 this.bodyEl = this.el.select('.modal-body',true).first();
2562 this.closeEl = this.el.select('.modal-header .close', true).first();
2563 this.footerEl = this.el.select('.modal-footer',true).first();
2564 this.titleEl = this.el.select('.modal-title',true).first();
2568 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2569 this.maskEl.enableDisplayMode("block");
2571 //this.el.addClass("x-dlg-modal");
2573 if (this.buttons.length) {
2574 Roo.each(this.buttons, function(bb) {
2575 var b = Roo.apply({}, bb);
2576 b.xns = b.xns || Roo.bootstrap;
2577 b.xtype = b.xtype || 'Button';
2578 if (typeof(b.listeners) == 'undefined') {
2579 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2582 var btn = Roo.factory(b);
2584 btn.render(this.el.select('.modal-footer div').first());
2588 // render the children.
2591 if(typeof(this.items) != 'undefined'){
2592 var items = this.items;
2595 for(var i =0;i < items.length;i++) {
2596 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2600 this.items = nitems;
2602 // where are these used - they used to be body/close/footer
2606 //this.el.addClass([this.fieldClass, this.cls]);
2610 getAutoCreate : function(){
2615 html : this.html || ''
2620 cls : 'modal-title',
2624 if(this.specificTitle){
2630 if (this.allow_close) {
2642 if(this.size.length){
2643 size = 'modal-' + this.size;
2648 style : 'display: none',
2651 cls: "modal-dialog " + size,
2654 cls : "modal-content",
2657 cls : 'modal-header',
2662 cls : 'modal-footer',
2666 cls: 'btn-' + this.buttonPosition
2683 modal.cls += ' fade';
2689 getChildContainer : function() {
2694 getButtonContainer : function() {
2695 return this.el.select('.modal-footer div',true).first();
2698 initEvents : function()
2700 if (this.allow_close) {
2701 this.closeEl.on('click', this.hide, this);
2703 Roo.EventManager.onWindowResize(this.resize, this, true);
2710 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2711 if (this.fitwindow) {
2712 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2713 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2718 setSize : function(w,h)
2728 if (!this.rendered) {
2732 this.el.setStyle('display', 'block');
2734 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2737 this.el.addClass('in');
2740 this.el.addClass('in');
2744 // not sure how we can show data in here..
2746 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2749 Roo.get(document.body).addClass("x-body-masked");
2750 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2752 this.el.setStyle('zIndex', '10001');
2754 this.fireEvent('show', this);
2759 this.items.forEach( function(e) {
2760 e.layout ? e.layout() : false;
2769 Roo.get(document.body).removeClass("x-body-masked");
2770 this.el.removeClass('in');
2771 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2773 if(this.animate){ // why
2775 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2777 this.el.setStyle('display', 'none');
2780 this.fireEvent('hide', this);
2783 addButton : function(str, cb)
2787 var b = Roo.apply({}, { html : str } );
2788 b.xns = b.xns || Roo.bootstrap;
2789 b.xtype = b.xtype || 'Button';
2790 if (typeof(b.listeners) == 'undefined') {
2791 b.listeners = { click : cb.createDelegate(this) };
2794 var btn = Roo.factory(b);
2796 btn.render(this.el.select('.modal-footer div').first());
2802 setDefaultButton : function(btn)
2804 //this.el.select('.modal-footer').()
2808 resizeTo: function(w,h)
2812 this.dialogEl.setWidth(w);
2813 if (this.diff === false) {
2814 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2817 this.bodyEl.setHeight(h-this.diff);
2821 setContentSize : function(w, h)
2825 onButtonClick: function(btn,e)
2828 this.fireEvent('btnclick', btn.name, e);
2831 * Set the title of the Dialog
2832 * @param {String} str new Title
2834 setTitle: function(str) {
2835 this.titleEl.dom.innerHTML = str;
2838 * Set the body of the Dialog
2839 * @param {String} str new Title
2841 setBody: function(str) {
2842 this.bodyEl.dom.innerHTML = str;
2845 * Set the body of the Dialog using the template
2846 * @param {Obj} data - apply this data to the template and replace the body contents.
2848 applyBody: function(obj)
2851 Roo.log("Error - using apply Body without a template");
2854 this.tmpl.overwrite(this.bodyEl, obj);
2860 Roo.apply(Roo.bootstrap.Modal, {
2862 * Button config that displays a single OK button
2871 * Button config that displays Yes and No buttons
2887 * Button config that displays OK and Cancel buttons
2902 * Button config that displays Yes, No and Cancel buttons
2925 * messagebox - can be used as a replace
2929 * @class Roo.MessageBox
2930 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2934 Roo.Msg.alert('Status', 'Changes saved successfully.');
2936 // Prompt for user data:
2937 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2939 // process text value...
2943 // Show a dialog using config options:
2945 title:'Save Changes?',
2946 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2947 buttons: Roo.Msg.YESNOCANCEL,
2954 Roo.bootstrap.MessageBox = function(){
2955 var dlg, opt, mask, waitTimer;
2956 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2957 var buttons, activeTextEl, bwidth;
2961 var handleButton = function(button){
2963 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2967 var handleHide = function(){
2969 dlg.el.removeClass(opt.cls);
2972 // Roo.TaskMgr.stop(waitTimer);
2973 // waitTimer = null;
2978 var updateButtons = function(b){
2981 buttons["ok"].hide();
2982 buttons["cancel"].hide();
2983 buttons["yes"].hide();
2984 buttons["no"].hide();
2985 //dlg.footer.dom.style.display = 'none';
2988 dlg.footerEl.dom.style.display = '';
2989 for(var k in buttons){
2990 if(typeof buttons[k] != "function"){
2993 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2994 width += buttons[k].el.getWidth()+15;
3004 var handleEsc = function(d, k, e){
3005 if(opt && opt.closable !== false){
3015 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3016 * @return {Roo.BasicDialog} The BasicDialog element
3018 getDialog : function(){
3020 dlg = new Roo.bootstrap.Modal( {
3023 //constraintoviewport:false,
3025 //collapsible : false,
3030 //buttonAlign:"center",
3031 closeClick : function(){
3032 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3035 handleButton("cancel");
3040 dlg.on("hide", handleHide);
3042 //dlg.addKeyListener(27, handleEsc);
3044 this.buttons = buttons;
3045 var bt = this.buttonText;
3046 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3047 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3048 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3049 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3051 bodyEl = dlg.bodyEl.createChild({
3053 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3054 '<textarea class="roo-mb-textarea"></textarea>' +
3055 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3057 msgEl = bodyEl.dom.firstChild;
3058 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3059 textboxEl.enableDisplayMode();
3060 textboxEl.addKeyListener([10,13], function(){
3061 if(dlg.isVisible() && opt && opt.buttons){
3064 }else if(opt.buttons.yes){
3065 handleButton("yes");
3069 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3070 textareaEl.enableDisplayMode();
3071 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3072 progressEl.enableDisplayMode();
3073 var pf = progressEl.dom.firstChild;
3075 pp = Roo.get(pf.firstChild);
3076 pp.setHeight(pf.offsetHeight);
3084 * Updates the message box body text
3085 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3086 * the XHTML-compliant non-breaking space character '&#160;')
3087 * @return {Roo.MessageBox} This message box
3089 updateText : function(text){
3090 if(!dlg.isVisible() && !opt.width){
3091 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3093 msgEl.innerHTML = text || ' ';
3095 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3096 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3098 Math.min(opt.width || cw , this.maxWidth),
3099 Math.max(opt.minWidth || this.minWidth, bwidth)
3102 activeTextEl.setWidth(w);
3104 if(dlg.isVisible()){
3105 dlg.fixedcenter = false;
3107 // to big, make it scroll. = But as usual stupid IE does not support
3110 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3111 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3112 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3114 bodyEl.dom.style.height = '';
3115 bodyEl.dom.style.overflowY = '';
3118 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3120 bodyEl.dom.style.overflowX = '';
3123 dlg.setContentSize(w, bodyEl.getHeight());
3124 if(dlg.isVisible()){
3125 dlg.fixedcenter = true;
3131 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3132 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3133 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3134 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3135 * @return {Roo.MessageBox} This message box
3137 updateProgress : function(value, text){
3139 this.updateText(text);
3141 if (pp) { // weird bug on my firefox - for some reason this is not defined
3142 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3148 * Returns true if the message box is currently displayed
3149 * @return {Boolean} True if the message box is visible, else false
3151 isVisible : function(){
3152 return dlg && dlg.isVisible();
3156 * Hides the message box if it is displayed
3159 if(this.isVisible()){
3165 * Displays a new message box, or reinitializes an existing message box, based on the config options
3166 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3167 * The following config object properties are supported:
3169 Property Type Description
3170 ---------- --------------- ------------------------------------------------------------------------------------
3171 animEl String/Element An id or Element from which the message box should animate as it opens and
3172 closes (defaults to undefined)
3173 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3174 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3175 closable Boolean False to hide the top-right close button (defaults to true). Note that
3176 progress and wait dialogs will ignore this property and always hide the
3177 close button as they can only be closed programmatically.
3178 cls String A custom CSS class to apply to the message box element
3179 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3180 displayed (defaults to 75)
3181 fn Function A callback function to execute after closing the dialog. The arguments to the
3182 function will be btn (the name of the button that was clicked, if applicable,
3183 e.g. "ok"), and text (the value of the active text field, if applicable).
3184 Progress and wait dialogs will ignore this option since they do not respond to
3185 user actions and can only be closed programmatically, so any required function
3186 should be called by the same code after it closes the dialog.
3187 icon String A CSS class that provides a background image to be used as an icon for
3188 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3189 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3190 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3191 modal Boolean False to allow user interaction with the page while the message box is
3192 displayed (defaults to true)
3193 msg String A string that will replace the existing message box body text (defaults
3194 to the XHTML-compliant non-breaking space character ' ')
3195 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3196 progress Boolean True to display a progress bar (defaults to false)
3197 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3198 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3199 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3200 title String The title text
3201 value String The string value to set into the active textbox element if displayed
3202 wait Boolean True to display a progress bar (defaults to false)
3203 width Number The width of the dialog in pixels
3210 msg: 'Please enter your address:',
3212 buttons: Roo.MessageBox.OKCANCEL,
3215 animEl: 'addAddressBtn'
3218 * @param {Object} config Configuration options
3219 * @return {Roo.MessageBox} This message box
3221 show : function(options)
3224 // this causes nightmares if you show one dialog after another
3225 // especially on callbacks..
3227 if(this.isVisible()){
3230 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3231 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3232 Roo.log("New Dialog Message:" + options.msg )
3233 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3234 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3237 var d = this.getDialog();
3239 d.setTitle(opt.title || " ");
3240 d.closeEl.setDisplayed(opt.closable !== false);
3241 activeTextEl = textboxEl;
3242 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3247 textareaEl.setHeight(typeof opt.multiline == "number" ?
3248 opt.multiline : this.defaultTextHeight);
3249 activeTextEl = textareaEl;
3258 progressEl.setDisplayed(opt.progress === true);
3259 this.updateProgress(0);
3260 activeTextEl.dom.value = opt.value || "";
3262 dlg.setDefaultButton(activeTextEl);
3264 var bs = opt.buttons;
3268 }else if(bs && bs.yes){
3269 db = buttons["yes"];
3271 dlg.setDefaultButton(db);
3273 bwidth = updateButtons(opt.buttons);
3274 this.updateText(opt.msg);
3276 d.el.addClass(opt.cls);
3278 d.proxyDrag = opt.proxyDrag === true;
3279 d.modal = opt.modal !== false;
3280 d.mask = opt.modal !== false ? mask : false;
3282 // force it to the end of the z-index stack so it gets a cursor in FF
3283 document.body.appendChild(dlg.el.dom);
3284 d.animateTarget = null;
3285 d.show(options.animEl);
3291 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3292 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3293 * and closing the message box when the process is complete.
3294 * @param {String} title The title bar text
3295 * @param {String} msg The message box body text
3296 * @return {Roo.MessageBox} This message box
3298 progress : function(title, msg){
3305 minWidth: this.minProgressWidth,
3312 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3313 * If a callback function is passed it will be called after the user clicks the button, and the
3314 * id of the button that was clicked will be passed as the only parameter to the callback
3315 * (could also be the top-right close button).
3316 * @param {String} title The title bar text
3317 * @param {String} msg The message box body text
3318 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3319 * @param {Object} scope (optional) The scope of the callback function
3320 * @return {Roo.MessageBox} This message box
3322 alert : function(title, msg, fn, scope){
3335 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3336 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3337 * You are responsible for closing the message box when the process is complete.
3338 * @param {String} msg The message box body text
3339 * @param {String} title (optional) The title bar text
3340 * @return {Roo.MessageBox} This message box
3342 wait : function(msg, title){
3353 waitTimer = Roo.TaskMgr.start({
3355 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3363 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3364 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3365 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3366 * @param {String} title The title bar text
3367 * @param {String} msg The message box body text
3368 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3369 * @param {Object} scope (optional) The scope of the callback function
3370 * @return {Roo.MessageBox} This message box
3372 confirm : function(title, msg, fn, scope){
3376 buttons: this.YESNO,
3385 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3386 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3387 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3388 * (could also be the top-right close button) and the text that was entered will be passed as the two
3389 * parameters to the callback.
3390 * @param {String} title The title bar text
3391 * @param {String} msg The message box body text
3392 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3393 * @param {Object} scope (optional) The scope of the callback function
3394 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3395 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3396 * @return {Roo.MessageBox} This message box
3398 prompt : function(title, msg, fn, scope, multiline){
3402 buttons: this.OKCANCEL,
3407 multiline: multiline,
3414 * Button config that displays a single OK button
3419 * Button config that displays Yes and No buttons
3422 YESNO : {yes:true, no:true},
3424 * Button config that displays OK and Cancel buttons
3427 OKCANCEL : {ok:true, cancel:true},
3429 * Button config that displays Yes, No and Cancel buttons
3432 YESNOCANCEL : {yes:true, no:true, cancel:true},
3435 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3438 defaultTextHeight : 75,
3440 * The maximum width in pixels of the message box (defaults to 600)
3445 * The minimum width in pixels of the message box (defaults to 100)
3450 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3451 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3454 minProgressWidth : 250,
3456 * An object containing the default button text strings that can be overriden for localized language support.
3457 * Supported properties are: ok, cancel, yes and no.
3458 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3471 * Shorthand for {@link Roo.MessageBox}
3473 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3474 Roo.Msg = Roo.Msg || Roo.MessageBox;
3483 * @class Roo.bootstrap.Navbar
3484 * @extends Roo.bootstrap.Component
3485 * Bootstrap Navbar class
3488 * Create a new Navbar
3489 * @param {Object} config The config object
3493 Roo.bootstrap.Navbar = function(config){
3494 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3498 * @event beforetoggle
3499 * Fire before toggle the menu
3500 * @param {Roo.EventObject} e
3502 "beforetoggle" : true
3506 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3515 getAutoCreate : function(){
3518 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3522 initEvents :function ()
3524 //Roo.log(this.el.select('.navbar-toggle',true));
3525 this.el.select('.navbar-toggle',true).on('click', function() {
3526 if(this.fireEvent('beforetoggle', this) !== false){
3527 this.el.select('.navbar-collapse',true).toggleClass('in');
3537 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3539 var size = this.el.getSize();
3540 this.maskEl.setSize(size.width, size.height);
3541 this.maskEl.enableDisplayMode("block");
3550 getChildContainer : function()
3552 if (this.el.select('.collapse').getCount()) {
3553 return this.el.select('.collapse',true).first();
3586 * @class Roo.bootstrap.NavSimplebar
3587 * @extends Roo.bootstrap.Navbar
3588 * Bootstrap Sidebar class
3590 * @cfg {Boolean} inverse is inverted color
3592 * @cfg {String} type (nav | pills | tabs)
3593 * @cfg {Boolean} arrangement stacked | justified
3594 * @cfg {String} align (left | right) alignment
3596 * @cfg {Boolean} main (true|false) main nav bar? default false
3597 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3599 * @cfg {String} tag (header|footer|nav|div) default is nav
3605 * Create a new Sidebar
3606 * @param {Object} config The config object
3610 Roo.bootstrap.NavSimplebar = function(config){
3611 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3614 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3630 getAutoCreate : function(){
3634 tag : this.tag || 'div',
3647 this.type = this.type || 'nav';
3648 if (['tabs','pills'].indexOf(this.type)!==-1) {
3649 cfg.cn[0].cls += ' nav-' + this.type
3653 if (this.type!=='nav') {
3654 Roo.log('nav type must be nav/tabs/pills')
3656 cfg.cn[0].cls += ' navbar-nav'
3662 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3663 cfg.cn[0].cls += ' nav-' + this.arrangement;
3667 if (this.align === 'right') {
3668 cfg.cn[0].cls += ' navbar-right';
3672 cfg.cls += ' navbar-inverse';
3699 * @class Roo.bootstrap.NavHeaderbar
3700 * @extends Roo.bootstrap.NavSimplebar
3701 * Bootstrap Sidebar class
3703 * @cfg {String} brand what is brand
3704 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3705 * @cfg {String} brand_href href of the brand
3706 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3707 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3708 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3709 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3712 * Create a new Sidebar
3713 * @param {Object} config The config object
3717 Roo.bootstrap.NavHeaderbar = function(config){
3718 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3722 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3729 desktopCenter : false,
3732 getAutoCreate : function(){
3735 tag: this.nav || 'nav',
3742 if (this.desktopCenter) {
3743 cn.push({cls : 'container', cn : []});
3750 cls: 'navbar-header',
3755 cls: 'navbar-toggle',
3756 'data-toggle': 'collapse',
3761 html: 'Toggle navigation'
3783 cls: 'collapse navbar-collapse',
3787 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3789 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3790 cfg.cls += ' navbar-' + this.position;
3792 // tag can override this..
3794 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3797 if (this.brand !== '') {
3800 href: this.brand_href ? this.brand_href : '#',
3801 cls: 'navbar-brand',
3809 cfg.cls += ' main-nav';
3817 getHeaderChildContainer : function()
3819 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3820 return this.el.select('.navbar-header',true).first();
3823 return this.getChildContainer();
3827 initEvents : function()
3829 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3831 if (this.autohide) {
3836 Roo.get(document).on('scroll',function(e) {
3837 var ns = Roo.get(document).getScroll().top;
3838 var os = prevScroll;
3842 ft.removeClass('slideDown');
3843 ft.addClass('slideUp');
3846 ft.removeClass('slideUp');
3847 ft.addClass('slideDown');
3868 * @class Roo.bootstrap.NavSidebar
3869 * @extends Roo.bootstrap.Navbar
3870 * Bootstrap Sidebar class
3873 * Create a new Sidebar
3874 * @param {Object} config The config object
3878 Roo.bootstrap.NavSidebar = function(config){
3879 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3882 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3884 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3886 getAutoCreate : function(){
3891 cls: 'sidebar sidebar-nav'
3913 * @class Roo.bootstrap.NavGroup
3914 * @extends Roo.bootstrap.Component
3915 * Bootstrap NavGroup class
3916 * @cfg {String} align (left|right)
3917 * @cfg {Boolean} inverse
3918 * @cfg {String} type (nav|pills|tab) default nav
3919 * @cfg {String} navId - reference Id for navbar.
3923 * Create a new nav group
3924 * @param {Object} config The config object
3927 Roo.bootstrap.NavGroup = function(config){
3928 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3931 Roo.bootstrap.NavGroup.register(this);
3935 * Fires when the active item changes
3936 * @param {Roo.bootstrap.NavGroup} this
3937 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3938 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3945 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3956 getAutoCreate : function()
3958 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3965 if (['tabs','pills'].indexOf(this.type)!==-1) {
3966 cfg.cls += ' nav-' + this.type
3968 if (this.type!=='nav') {
3969 Roo.log('nav type must be nav/tabs/pills')
3971 cfg.cls += ' navbar-nav'
3974 if (this.parent().sidebar) {
3977 cls: 'dashboard-menu sidebar-menu'
3983 if (this.form === true) {
3989 if (this.align === 'right') {
3990 cfg.cls += ' navbar-right';
3992 cfg.cls += ' navbar-left';
3996 if (this.align === 'right') {
3997 cfg.cls += ' navbar-right';
4001 cfg.cls += ' navbar-inverse';
4009 * sets the active Navigation item
4010 * @param {Roo.bootstrap.NavItem} the new current navitem
4012 setActiveItem : function(item)
4015 Roo.each(this.navItems, function(v){
4020 v.setActive(false, true);
4027 item.setActive(true, true);
4028 this.fireEvent('changed', this, item, prev);
4033 * gets the active Navigation item
4034 * @return {Roo.bootstrap.NavItem} the current navitem
4036 getActive : function()
4040 Roo.each(this.navItems, function(v){
4051 indexOfNav : function()
4055 Roo.each(this.navItems, function(v,i){
4066 * adds a Navigation item
4067 * @param {Roo.bootstrap.NavItem} the navitem to add
4069 addItem : function(cfg)
4071 var cn = new Roo.bootstrap.NavItem(cfg);
4073 cn.parentId = this.id;
4074 cn.onRender(this.el, null);
4078 * register a Navigation item
4079 * @param {Roo.bootstrap.NavItem} the navitem to add
4081 register : function(item)
4083 this.navItems.push( item);
4084 item.navId = this.navId;
4089 * clear all the Navigation item
4092 clearAll : function()
4095 this.el.dom.innerHTML = '';
4098 getNavItem: function(tabId)
4101 Roo.each(this.navItems, function(e) {
4102 if (e.tabId == tabId) {
4112 setActiveNext : function()
4114 var i = this.indexOfNav(this.getActive());
4115 if (i > this.navItems.length) {
4118 this.setActiveItem(this.navItems[i+1]);
4120 setActivePrev : function()
4122 var i = this.indexOfNav(this.getActive());
4126 this.setActiveItem(this.navItems[i-1]);
4128 clearWasActive : function(except) {
4129 Roo.each(this.navItems, function(e) {
4130 if (e.tabId != except.tabId && e.was_active) {
4131 e.was_active = false;
4138 getWasActive : function ()
4141 Roo.each(this.navItems, function(e) {
4156 Roo.apply(Roo.bootstrap.NavGroup, {
4160 * register a Navigation Group
4161 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4163 register : function(navgrp)
4165 this.groups[navgrp.navId] = navgrp;
4169 * fetch a Navigation Group based on the navigation ID
4170 * @param {string} the navgroup to add
4171 * @returns {Roo.bootstrap.NavGroup} the navgroup
4173 get: function(navId) {
4174 if (typeof(this.groups[navId]) == 'undefined') {
4176 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4178 return this.groups[navId] ;
4193 * @class Roo.bootstrap.NavItem
4194 * @extends Roo.bootstrap.Component
4195 * Bootstrap Navbar.NavItem class
4196 * @cfg {String} href link to
4197 * @cfg {String} html content of button
4198 * @cfg {String} badge text inside badge
4199 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4200 * @cfg {String} glyphicon name of glyphicon
4201 * @cfg {String} icon name of font awesome icon
4202 * @cfg {Boolean} active Is item active
4203 * @cfg {Boolean} disabled Is item disabled
4205 * @cfg {Boolean} preventDefault (true | false) default false
4206 * @cfg {String} tabId the tab that this item activates.
4207 * @cfg {String} tagtype (a|span) render as a href or span?
4208 * @cfg {Boolean} animateRef (true|false) link to element default false
4211 * Create a new Navbar Item
4212 * @param {Object} config The config object
4214 Roo.bootstrap.NavItem = function(config){
4215 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4220 * The raw click event for the entire grid.
4221 * @param {Roo.EventObject} e
4226 * Fires when the active item active state changes
4227 * @param {Roo.bootstrap.NavItem} this
4228 * @param {boolean} state the new state
4234 * Fires when scroll to element
4235 * @param {Roo.bootstrap.NavItem} this
4236 * @param {Object} options
4237 * @param {Roo.EventObject} e
4245 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4253 preventDefault : false,
4260 getAutoCreate : function(){
4269 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4271 if (this.disabled) {
4272 cfg.cls += ' disabled';
4275 if (this.href || this.html || this.glyphicon || this.icon) {
4279 href : this.href || "#",
4280 html: this.html || ''
4285 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4288 if(this.glyphicon) {
4289 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4294 cfg.cn[0].html += " <span class='caret'></span>";
4298 if (this.badge !== '') {
4300 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4308 initEvents: function()
4310 if (typeof (this.menu) != 'undefined') {
4311 this.menu.parentType = this.xtype;
4312 this.menu.triggerEl = this.el;
4313 this.menu = this.addxtype(Roo.apply({}, this.menu));
4316 this.el.select('a',true).on('click', this.onClick, this);
4318 if(this.tagtype == 'span'){
4319 this.el.select('span',true).on('click', this.onClick, this);
4322 // at this point parent should be available..
4323 this.parent().register(this);
4326 onClick : function(e)
4328 if (e.getTarget('.dropdown-menu-item')) {
4329 // did you click on a menu itemm.... - then don't trigger onclick..
4334 this.preventDefault ||
4337 Roo.log("NavItem - prevent Default?");
4341 if (this.disabled) {
4345 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4346 if (tg && tg.transition) {
4347 Roo.log("waiting for the transitionend");
4353 //Roo.log("fire event clicked");
4354 if(this.fireEvent('click', this, e) === false){
4358 if(this.tagtype == 'span'){
4362 //Roo.log(this.href);
4363 var ael = this.el.select('a',true).first();
4366 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4367 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4368 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4369 return; // ignore... - it's a 'hash' to another page.
4371 Roo.log("NavItem - prevent Default?");
4373 this.scrollToElement(e);
4377 var p = this.parent();
4379 if (['tabs','pills'].indexOf(p.type)!==-1) {
4380 if (typeof(p.setActiveItem) !== 'undefined') {
4381 p.setActiveItem(this);
4385 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4386 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4387 // remove the collapsed menu expand...
4388 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4392 isActive: function () {
4395 setActive : function(state, fire, is_was_active)
4397 if (this.active && !state && this.navId) {
4398 this.was_active = true;
4399 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4401 nv.clearWasActive(this);
4405 this.active = state;
4408 this.el.removeClass('active');
4409 } else if (!this.el.hasClass('active')) {
4410 this.el.addClass('active');
4413 this.fireEvent('changed', this, state);
4416 // show a panel if it's registered and related..
4418 if (!this.navId || !this.tabId || !state || is_was_active) {
4422 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4426 var pan = tg.getPanelByName(this.tabId);
4430 // if we can not flip to new panel - go back to old nav highlight..
4431 if (false == tg.showPanel(pan)) {
4432 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4434 var onav = nv.getWasActive();
4436 onav.setActive(true, false, true);
4445 // this should not be here...
4446 setDisabled : function(state)
4448 this.disabled = state;
4450 this.el.removeClass('disabled');
4451 } else if (!this.el.hasClass('disabled')) {
4452 this.el.addClass('disabled');
4458 * Fetch the element to display the tooltip on.
4459 * @return {Roo.Element} defaults to this.el
4461 tooltipEl : function()
4463 return this.el.select('' + this.tagtype + '', true).first();
4466 scrollToElement : function(e)
4468 var c = document.body;
4471 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4473 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4474 c = document.documentElement;
4477 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4483 var o = target.calcOffsetsTo(c);
4490 this.fireEvent('scrollto', this, options, e);
4492 Roo.get(c).scrollTo('top', options.value, true);
4505 * <span> icon </span>
4506 * <span> text </span>
4507 * <span>badge </span>
4511 * @class Roo.bootstrap.NavSidebarItem
4512 * @extends Roo.bootstrap.NavItem
4513 * Bootstrap Navbar.NavSidebarItem class
4514 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4515 * {bool} open is the menu open
4517 * Create a new Navbar Button
4518 * @param {Object} config The config object
4520 Roo.bootstrap.NavSidebarItem = function(config){
4521 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4526 * The raw click event for the entire grid.
4527 * @param {Roo.EventObject} e
4532 * Fires when the active item active state changes
4533 * @param {Roo.bootstrap.NavSidebarItem} this
4534 * @param {boolean} state the new state
4542 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4544 badgeWeight : 'default',
4548 getAutoCreate : function(){
4553 href : this.href || '#',
4565 html : this.html || ''
4570 cfg.cls += ' active';
4573 if (this.disabled) {
4574 cfg.cls += ' disabled';
4577 cfg.cls += ' open x-open';
4580 if (this.glyphicon || this.icon) {
4581 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4582 a.cn.push({ tag : 'i', cls : c }) ;
4587 if (this.badge !== '') {
4589 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4593 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4594 a.cls += 'dropdown-toggle treeview' ;
4602 initEvents : function()
4604 if (typeof (this.menu) != 'undefined') {
4605 this.menu.parentType = this.xtype;
4606 this.menu.triggerEl = this.el;
4607 this.menu = this.addxtype(Roo.apply({}, this.menu));
4610 this.el.on('click', this.onClick, this);
4613 if(this.badge !== ''){
4615 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4620 onClick : function(e)
4627 if(this.preventDefault){
4631 this.fireEvent('click', this);
4634 disable : function()
4636 this.setDisabled(true);
4641 this.setDisabled(false);
4644 setDisabled : function(state)
4646 if(this.disabled == state){
4650 this.disabled = state;
4653 this.el.addClass('disabled');
4657 this.el.removeClass('disabled');
4662 setActive : function(state)
4664 if(this.active == state){
4668 this.active = state;
4671 this.el.addClass('active');
4675 this.el.removeClass('active');
4680 isActive: function ()
4685 setBadge : function(str)
4691 this.badgeEl.dom.innerHTML = str;
4708 * @class Roo.bootstrap.Row
4709 * @extends Roo.bootstrap.Component
4710 * Bootstrap Row class (contains columns...)
4714 * @param {Object} config The config object
4717 Roo.bootstrap.Row = function(config){
4718 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4721 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4723 getAutoCreate : function(){
4742 * @class Roo.bootstrap.Element
4743 * @extends Roo.bootstrap.Component
4744 * Bootstrap Element class
4745 * @cfg {String} html contents of the element
4746 * @cfg {String} tag tag of the element
4747 * @cfg {String} cls class of the element
4748 * @cfg {Boolean} preventDefault (true|false) default false
4749 * @cfg {Boolean} clickable (true|false) default false
4752 * Create a new Element
4753 * @param {Object} config The config object
4756 Roo.bootstrap.Element = function(config){
4757 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4763 * When a element is chick
4764 * @param {Roo.bootstrap.Element} this
4765 * @param {Roo.EventObject} e
4771 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4776 preventDefault: false,
4779 getAutoCreate : function(){
4790 initEvents: function()
4792 Roo.bootstrap.Element.superclass.initEvents.call(this);
4795 this.el.on('click', this.onClick, this);
4800 onClick : function(e)
4802 if(this.preventDefault){
4806 this.fireEvent('click', this, e);
4809 getValue : function()
4811 return this.el.dom.innerHTML;
4814 setValue : function(value)
4816 this.el.dom.innerHTML = value;
4831 * @class Roo.bootstrap.Pagination
4832 * @extends Roo.bootstrap.Component
4833 * Bootstrap Pagination class
4834 * @cfg {String} size xs | sm | md | lg
4835 * @cfg {Boolean} inverse false | true
4838 * Create a new Pagination
4839 * @param {Object} config The config object
4842 Roo.bootstrap.Pagination = function(config){
4843 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4846 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4852 getAutoCreate : function(){
4858 cfg.cls += ' inverse';
4864 cfg.cls += " " + this.cls;
4882 * @class Roo.bootstrap.PaginationItem
4883 * @extends Roo.bootstrap.Component
4884 * Bootstrap PaginationItem class
4885 * @cfg {String} html text
4886 * @cfg {String} href the link
4887 * @cfg {Boolean} preventDefault (true | false) default true
4888 * @cfg {Boolean} active (true | false) default false
4889 * @cfg {Boolean} disabled default false
4893 * Create a new PaginationItem
4894 * @param {Object} config The config object
4898 Roo.bootstrap.PaginationItem = function(config){
4899 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4904 * The raw click event for the entire grid.
4905 * @param {Roo.EventObject} e
4911 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4915 preventDefault: true,
4920 getAutoCreate : function(){
4926 href : this.href ? this.href : '#',
4927 html : this.html ? this.html : ''
4937 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4941 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4947 initEvents: function() {
4949 this.el.on('click', this.onClick, this);
4952 onClick : function(e)
4954 Roo.log('PaginationItem on click ');
4955 if(this.preventDefault){
4963 this.fireEvent('click', this, e);
4979 * @class Roo.bootstrap.Slider
4980 * @extends Roo.bootstrap.Component
4981 * Bootstrap Slider class
4984 * Create a new Slider
4985 * @param {Object} config The config object
4988 Roo.bootstrap.Slider = function(config){
4989 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4992 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4994 getAutoCreate : function(){
4998 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5002 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5014 * Ext JS Library 1.1.1
5015 * Copyright(c) 2006-2007, Ext JS, LLC.
5017 * Originally Released Under LGPL - original licence link has changed is not relivant.
5020 * <script type="text/javascript">
5025 * @class Roo.grid.ColumnModel
5026 * @extends Roo.util.Observable
5027 * This is the default implementation of a ColumnModel used by the Grid. It defines
5028 * the columns in the grid.
5031 var colModel = new Roo.grid.ColumnModel([
5032 {header: "Ticker", width: 60, sortable: true, locked: true},
5033 {header: "Company Name", width: 150, sortable: true},
5034 {header: "Market Cap.", width: 100, sortable: true},
5035 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5036 {header: "Employees", width: 100, sortable: true, resizable: false}
5041 * The config options listed for this class are options which may appear in each
5042 * individual column definition.
5043 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5045 * @param {Object} config An Array of column config objects. See this class's
5046 * config objects for details.
5048 Roo.grid.ColumnModel = function(config){
5050 * The config passed into the constructor
5052 this.config = config;
5055 // if no id, create one
5056 // if the column does not have a dataIndex mapping,
5057 // map it to the order it is in the config
5058 for(var i = 0, len = config.length; i < len; i++){
5060 if(typeof c.dataIndex == "undefined"){
5063 if(typeof c.renderer == "string"){
5064 c.renderer = Roo.util.Format[c.renderer];
5066 if(typeof c.id == "undefined"){
5069 if(c.editor && c.editor.xtype){
5070 c.editor = Roo.factory(c.editor, Roo.grid);
5072 if(c.editor && c.editor.isFormField){
5073 c.editor = new Roo.grid.GridEditor(c.editor);
5075 this.lookup[c.id] = c;
5079 * The width of columns which have no width specified (defaults to 100)
5082 this.defaultWidth = 100;
5085 * Default sortable of columns which have no sortable specified (defaults to false)
5088 this.defaultSortable = false;
5092 * @event widthchange
5093 * Fires when the width of a column changes.
5094 * @param {ColumnModel} this
5095 * @param {Number} columnIndex The column index
5096 * @param {Number} newWidth The new width
5098 "widthchange": true,
5100 * @event headerchange
5101 * Fires when the text of a header changes.
5102 * @param {ColumnModel} this
5103 * @param {Number} columnIndex The column index
5104 * @param {Number} newText The new header text
5106 "headerchange": true,
5108 * @event hiddenchange
5109 * Fires when a column is hidden or "unhidden".
5110 * @param {ColumnModel} this
5111 * @param {Number} columnIndex The column index
5112 * @param {Boolean} hidden true if hidden, false otherwise
5114 "hiddenchange": true,
5116 * @event columnmoved
5117 * Fires when a column is moved.
5118 * @param {ColumnModel} this
5119 * @param {Number} oldIndex
5120 * @param {Number} newIndex
5122 "columnmoved" : true,
5124 * @event columlockchange
5125 * Fires when a column's locked state is changed
5126 * @param {ColumnModel} this
5127 * @param {Number} colIndex
5128 * @param {Boolean} locked true if locked
5130 "columnlockchange" : true
5132 Roo.grid.ColumnModel.superclass.constructor.call(this);
5134 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5136 * @cfg {String} header The header text to display in the Grid view.
5139 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5140 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5141 * specified, the column's index is used as an index into the Record's data Array.
5144 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5145 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5148 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5149 * Defaults to the value of the {@link #defaultSortable} property.
5150 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5153 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5156 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5159 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5162 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5165 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5166 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5167 * default renderer uses the raw data value. If an object is returned (bootstrap only)
5168 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5171 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5174 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5177 * @cfg {String} cursor (Optional)
5180 * @cfg {String} tooltip (Optional)
5183 * @cfg {Number} xs (Optional)
5186 * @cfg {Number} sm (Optional)
5189 * @cfg {Number} md (Optional)
5192 * @cfg {Number} lg (Optional)
5195 * Returns the id of the column at the specified index.
5196 * @param {Number} index The column index
5197 * @return {String} the id
5199 getColumnId : function(index){
5200 return this.config[index].id;
5204 * Returns the column for a specified id.
5205 * @param {String} id The column id
5206 * @return {Object} the column
5208 getColumnById : function(id){
5209 return this.lookup[id];
5214 * Returns the column for a specified dataIndex.
5215 * @param {String} dataIndex The column dataIndex
5216 * @return {Object|Boolean} the column or false if not found
5218 getColumnByDataIndex: function(dataIndex){
5219 var index = this.findColumnIndex(dataIndex);
5220 return index > -1 ? this.config[index] : false;
5224 * Returns the index for a specified column id.
5225 * @param {String} id The column id
5226 * @return {Number} the index, or -1 if not found
5228 getIndexById : function(id){
5229 for(var i = 0, len = this.config.length; i < len; i++){
5230 if(this.config[i].id == id){
5238 * Returns the index for a specified column dataIndex.
5239 * @param {String} dataIndex The column dataIndex
5240 * @return {Number} the index, or -1 if not found
5243 findColumnIndex : function(dataIndex){
5244 for(var i = 0, len = this.config.length; i < len; i++){
5245 if(this.config[i].dataIndex == dataIndex){
5253 moveColumn : function(oldIndex, newIndex){
5254 var c = this.config[oldIndex];
5255 this.config.splice(oldIndex, 1);
5256 this.config.splice(newIndex, 0, c);
5257 this.dataMap = null;
5258 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5261 isLocked : function(colIndex){
5262 return this.config[colIndex].locked === true;
5265 setLocked : function(colIndex, value, suppressEvent){
5266 if(this.isLocked(colIndex) == value){
5269 this.config[colIndex].locked = value;
5271 this.fireEvent("columnlockchange", this, colIndex, value);
5275 getTotalLockedWidth : function(){
5277 for(var i = 0; i < this.config.length; i++){
5278 if(this.isLocked(i) && !this.isHidden(i)){
5279 this.totalWidth += this.getColumnWidth(i);
5285 getLockedCount : function(){
5286 for(var i = 0, len = this.config.length; i < len; i++){
5287 if(!this.isLocked(i)){
5292 return this.config.length;
5296 * Returns the number of columns.
5299 getColumnCount : function(visibleOnly){
5300 if(visibleOnly === true){
5302 for(var i = 0, len = this.config.length; i < len; i++){
5303 if(!this.isHidden(i)){
5309 return this.config.length;
5313 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5314 * @param {Function} fn
5315 * @param {Object} scope (optional)
5316 * @return {Array} result
5318 getColumnsBy : function(fn, scope){
5320 for(var i = 0, len = this.config.length; i < len; i++){
5321 var c = this.config[i];
5322 if(fn.call(scope||this, c, i) === true){
5330 * Returns true if the specified column is sortable.
5331 * @param {Number} col The column index
5334 isSortable : function(col){
5335 if(typeof this.config[col].sortable == "undefined"){
5336 return this.defaultSortable;
5338 return this.config[col].sortable;
5342 * Returns the rendering (formatting) function defined for the column.
5343 * @param {Number} col The column index.
5344 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5346 getRenderer : function(col){
5347 if(!this.config[col].renderer){
5348 return Roo.grid.ColumnModel.defaultRenderer;
5350 return this.config[col].renderer;
5354 * Sets the rendering (formatting) function for a column.
5355 * @param {Number} col The column index
5356 * @param {Function} fn The function to use to process the cell's raw data
5357 * to return HTML markup for the grid view. The render function is called with
5358 * the following parameters:<ul>
5359 * <li>Data value.</li>
5360 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5361 * <li>css A CSS style string to apply to the table cell.</li>
5362 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5363 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5364 * <li>Row index</li>
5365 * <li>Column index</li>
5366 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5368 setRenderer : function(col, fn){
5369 this.config[col].renderer = fn;
5373 * Returns the width for the specified column.
5374 * @param {Number} col The column index
5377 getColumnWidth : function(col){
5378 return this.config[col].width * 1 || this.defaultWidth;
5382 * Sets the width for a column.
5383 * @param {Number} col The column index
5384 * @param {Number} width The new width
5386 setColumnWidth : function(col, width, suppressEvent){
5387 this.config[col].width = width;
5388 this.totalWidth = null;
5390 this.fireEvent("widthchange", this, col, width);
5395 * Returns the total width of all columns.
5396 * @param {Boolean} includeHidden True to include hidden column widths
5399 getTotalWidth : function(includeHidden){
5400 if(!this.totalWidth){
5401 this.totalWidth = 0;
5402 for(var i = 0, len = this.config.length; i < len; i++){
5403 if(includeHidden || !this.isHidden(i)){
5404 this.totalWidth += this.getColumnWidth(i);
5408 return this.totalWidth;
5412 * Returns the header for the specified column.
5413 * @param {Number} col The column index
5416 getColumnHeader : function(col){
5417 return this.config[col].header;
5421 * Sets the header for a column.
5422 * @param {Number} col The column index
5423 * @param {String} header The new header
5425 setColumnHeader : function(col, header){
5426 this.config[col].header = header;
5427 this.fireEvent("headerchange", this, col, header);
5431 * Returns the tooltip for the specified column.
5432 * @param {Number} col The column index
5435 getColumnTooltip : function(col){
5436 return this.config[col].tooltip;
5439 * Sets the tooltip for a column.
5440 * @param {Number} col The column index
5441 * @param {String} tooltip The new tooltip
5443 setColumnTooltip : function(col, tooltip){
5444 this.config[col].tooltip = tooltip;
5448 * Returns the dataIndex for the specified column.
5449 * @param {Number} col The column index
5452 getDataIndex : function(col){
5453 return this.config[col].dataIndex;
5457 * Sets the dataIndex for a column.
5458 * @param {Number} col The column index
5459 * @param {Number} dataIndex The new dataIndex
5461 setDataIndex : function(col, dataIndex){
5462 this.config[col].dataIndex = dataIndex;
5468 * Returns true if the cell is editable.
5469 * @param {Number} colIndex The column index
5470 * @param {Number} rowIndex The row index - this is nto actually used..?
5473 isCellEditable : function(colIndex, rowIndex){
5474 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5478 * Returns the editor defined for the cell/column.
5479 * return false or null to disable editing.
5480 * @param {Number} colIndex The column index
5481 * @param {Number} rowIndex The row index
5484 getCellEditor : function(colIndex, rowIndex){
5485 return this.config[colIndex].editor;
5489 * Sets if a column is editable.
5490 * @param {Number} col The column index
5491 * @param {Boolean} editable True if the column is editable
5493 setEditable : function(col, editable){
5494 this.config[col].editable = editable;
5499 * Returns true if the column is hidden.
5500 * @param {Number} colIndex The column index
5503 isHidden : function(colIndex){
5504 return this.config[colIndex].hidden;
5509 * Returns true if the column width cannot be changed
5511 isFixed : function(colIndex){
5512 return this.config[colIndex].fixed;
5516 * Returns true if the column can be resized
5519 isResizable : function(colIndex){
5520 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5523 * Sets if a column is hidden.
5524 * @param {Number} colIndex The column index
5525 * @param {Boolean} hidden True if the column is hidden
5527 setHidden : function(colIndex, hidden){
5528 this.config[colIndex].hidden = hidden;
5529 this.totalWidth = null;
5530 this.fireEvent("hiddenchange", this, colIndex, hidden);
5534 * Sets the editor for a column.
5535 * @param {Number} col The column index
5536 * @param {Object} editor The editor object
5538 setEditor : function(col, editor){
5539 this.config[col].editor = editor;
5543 Roo.grid.ColumnModel.defaultRenderer = function(value){
5544 if(typeof value == "string" && value.length < 1){
5550 // Alias for backwards compatibility
5551 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5554 * Ext JS Library 1.1.1
5555 * Copyright(c) 2006-2007, Ext JS, LLC.
5557 * Originally Released Under LGPL - original licence link has changed is not relivant.
5560 * <script type="text/javascript">
5564 * @class Roo.LoadMask
5565 * A simple utility class for generically masking elements while loading data. If the element being masked has
5566 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5567 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5568 * element's UpdateManager load indicator and will be destroyed after the initial load.
5570 * Create a new LoadMask
5571 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5572 * @param {Object} config The config object
5574 Roo.LoadMask = function(el, config){
5575 this.el = Roo.get(el);
5576 Roo.apply(this, config);
5578 this.store.on('beforeload', this.onBeforeLoad, this);
5579 this.store.on('load', this.onLoad, this);
5580 this.store.on('loadexception', this.onLoadException, this);
5581 this.removeMask = false;
5583 var um = this.el.getUpdateManager();
5584 um.showLoadIndicator = false; // disable the default indicator
5585 um.on('beforeupdate', this.onBeforeLoad, this);
5586 um.on('update', this.onLoad, this);
5587 um.on('failure', this.onLoad, this);
5588 this.removeMask = true;
5592 Roo.LoadMask.prototype = {
5594 * @cfg {Boolean} removeMask
5595 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5596 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5600 * The text to display in a centered loading message box (defaults to 'Loading...')
5604 * @cfg {String} msgCls
5605 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5607 msgCls : 'x-mask-loading',
5610 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5616 * Disables the mask to prevent it from being displayed
5618 disable : function(){
5619 this.disabled = true;
5623 * Enables the mask so that it can be displayed
5625 enable : function(){
5626 this.disabled = false;
5629 onLoadException : function()
5633 if (typeof(arguments[3]) != 'undefined') {
5634 Roo.MessageBox.alert("Error loading",arguments[3]);
5638 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5639 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5648 this.el.unmask(this.removeMask);
5653 this.el.unmask(this.removeMask);
5657 onBeforeLoad : function(){
5659 this.el.mask(this.msg, this.msgCls);
5664 destroy : function(){
5666 this.store.un('beforeload', this.onBeforeLoad, this);
5667 this.store.un('load', this.onLoad, this);
5668 this.store.un('loadexception', this.onLoadException, this);
5670 var um = this.el.getUpdateManager();
5671 um.un('beforeupdate', this.onBeforeLoad, this);
5672 um.un('update', this.onLoad, this);
5673 um.un('failure', this.onLoad, this);
5684 * @class Roo.bootstrap.Table
5685 * @extends Roo.bootstrap.Component
5686 * Bootstrap Table class
5687 * @cfg {String} cls table class
5688 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5689 * @cfg {String} bgcolor Specifies the background color for a table
5690 * @cfg {Number} border Specifies whether the table cells should have borders or not
5691 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5692 * @cfg {Number} cellspacing Specifies the space between cells
5693 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5694 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5695 * @cfg {String} sortable Specifies that the table should be sortable
5696 * @cfg {String} summary Specifies a summary of the content of a table
5697 * @cfg {Number} width Specifies the width of a table
5698 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5700 * @cfg {boolean} striped Should the rows be alternative striped
5701 * @cfg {boolean} bordered Add borders to the table
5702 * @cfg {boolean} hover Add hover highlighting
5703 * @cfg {boolean} condensed Format condensed
5704 * @cfg {boolean} responsive Format condensed
5705 * @cfg {Boolean} loadMask (true|false) default false
5706 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5707 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5708 * @cfg {Boolean} rowSelection (true|false) default false
5709 * @cfg {Boolean} cellSelection (true|false) default false
5710 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5711 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5715 * Create a new Table
5716 * @param {Object} config The config object
5719 Roo.bootstrap.Table = function(config){
5720 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5725 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5726 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5727 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5728 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5730 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5732 this.sm.grid = this;
5733 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5734 this.sm = this.selModel;
5735 this.sm.xmodule = this.xmodule || false;
5738 if (this.cm && typeof(this.cm.config) == 'undefined') {
5739 this.colModel = new Roo.grid.ColumnModel(this.cm);
5740 this.cm = this.colModel;
5741 this.cm.xmodule = this.xmodule || false;
5744 this.store= Roo.factory(this.store, Roo.data);
5745 this.ds = this.store;
5746 this.ds.xmodule = this.xmodule || false;
5749 if (this.footer && this.store) {
5750 this.footer.dataSource = this.ds;
5751 this.footer = Roo.factory(this.footer);
5758 * Fires when a cell is clicked
5759 * @param {Roo.bootstrap.Table} this
5760 * @param {Roo.Element} el
5761 * @param {Number} rowIndex
5762 * @param {Number} columnIndex
5763 * @param {Roo.EventObject} e
5767 * @event celldblclick
5768 * Fires when a cell is double clicked
5769 * @param {Roo.bootstrap.Table} this
5770 * @param {Roo.Element} el
5771 * @param {Number} rowIndex
5772 * @param {Number} columnIndex
5773 * @param {Roo.EventObject} e
5775 "celldblclick" : true,
5778 * Fires when a row is clicked
5779 * @param {Roo.bootstrap.Table} this
5780 * @param {Roo.Element} el
5781 * @param {Number} rowIndex
5782 * @param {Roo.EventObject} e
5786 * @event rowdblclick
5787 * Fires when a row is double clicked
5788 * @param {Roo.bootstrap.Table} this
5789 * @param {Roo.Element} el
5790 * @param {Number} rowIndex
5791 * @param {Roo.EventObject} e
5793 "rowdblclick" : true,
5796 * Fires when a mouseover occur
5797 * @param {Roo.bootstrap.Table} this
5798 * @param {Roo.Element} el
5799 * @param {Number} rowIndex
5800 * @param {Number} columnIndex
5801 * @param {Roo.EventObject} e
5806 * Fires when a mouseout occur
5807 * @param {Roo.bootstrap.Table} this
5808 * @param {Roo.Element} el
5809 * @param {Number} rowIndex
5810 * @param {Number} columnIndex
5811 * @param {Roo.EventObject} e
5816 * Fires when a row is rendered, so you can change add a style to it.
5817 * @param {Roo.bootstrap.Table} this
5818 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5822 * @event rowsrendered
5823 * Fires when all the rows have been rendered
5824 * @param {Roo.bootstrap.Table} this
5826 'rowsrendered' : true,
5828 * @event contextmenu
5829 * The raw contextmenu event for the entire grid.
5830 * @param {Roo.EventObject} e
5832 "contextmenu" : true,
5834 * @event rowcontextmenu
5835 * Fires when a row is right clicked
5836 * @param {Roo.bootstrap.Table} this
5837 * @param {Number} rowIndex
5838 * @param {Roo.EventObject} e
5840 "rowcontextmenu" : true,
5842 * @event cellcontextmenu
5843 * Fires when a cell is right clicked
5844 * @param {Roo.bootstrap.Table} this
5845 * @param {Number} rowIndex
5846 * @param {Number} cellIndex
5847 * @param {Roo.EventObject} e
5849 "cellcontextmenu" : true,
5851 * @event headercontextmenu
5852 * Fires when a header is right clicked
5853 * @param {Roo.bootstrap.Table} this
5854 * @param {Number} columnIndex
5855 * @param {Roo.EventObject} e
5857 "headercontextmenu" : true
5861 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5887 rowSelection : false,
5888 cellSelection : false,
5891 // Roo.Element - the tbody
5893 // Roo.Element - thead element
5896 container: false, // used by gridpanel...
5898 getAutoCreate : function()
5900 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5907 if (this.scrollBody) {
5908 cfg.cls += ' table-body-fixed';
5911 cfg.cls += ' table-striped';
5915 cfg.cls += ' table-hover';
5917 if (this.bordered) {
5918 cfg.cls += ' table-bordered';
5920 if (this.condensed) {
5921 cfg.cls += ' table-condensed';
5923 if (this.responsive) {
5924 cfg.cls += ' table-responsive';
5928 cfg.cls+= ' ' +this.cls;
5931 // this lot should be simplifed...
5934 cfg.align=this.align;
5937 cfg.bgcolor=this.bgcolor;
5940 cfg.border=this.border;
5942 if (this.cellpadding) {
5943 cfg.cellpadding=this.cellpadding;
5945 if (this.cellspacing) {
5946 cfg.cellspacing=this.cellspacing;
5949 cfg.frame=this.frame;
5952 cfg.rules=this.rules;
5954 if (this.sortable) {
5955 cfg.sortable=this.sortable;
5958 cfg.summary=this.summary;
5961 cfg.width=this.width;
5964 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5967 if(this.store || this.cm){
5968 if(this.headerShow){
5969 cfg.cn.push(this.renderHeader());
5972 cfg.cn.push(this.renderBody());
5974 if(this.footerShow){
5975 cfg.cn.push(this.renderFooter());
5977 // where does this come from?
5978 //cfg.cls+= ' TableGrid';
5981 return { cn : [ cfg ] };
5984 initEvents : function()
5986 if(!this.store || !this.cm){
5989 if (this.selModel) {
5990 this.selModel.initEvents();
5994 //Roo.log('initEvents with ds!!!!');
5996 this.mainBody = this.el.select('tbody', true).first();
5997 this.mainHead = this.el.select('thead', true).first();
6004 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6005 e.on('click', _this.sort, _this);
6008 this.el.on("click", this.onClick, this);
6009 this.el.on("dblclick", this.onDblClick, this);
6011 // why is this done????? = it breaks dialogs??
6012 //this.parent().el.setStyle('position', 'relative');
6016 this.footer.parentId = this.id;
6017 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6020 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6022 this.store.on('load', this.onLoad, this);
6023 this.store.on('beforeload', this.onBeforeLoad, this);
6024 this.store.on('update', this.onUpdate, this);
6025 this.store.on('add', this.onAdd, this);
6026 this.store.on("clear", this.clear, this);
6028 this.el.on("contextmenu", this.onContextMenu, this);
6030 this.mainBody.on('scroll', this.onBodyScroll, this);
6035 onContextMenu : function(e, t)
6037 this.processEvent("contextmenu", e);
6040 processEvent : function(name, e)
6042 if (name != 'touchstart' ) {
6043 this.fireEvent(name, e);
6046 var t = e.getTarget();
6048 var cell = Roo.get(t);
6054 if(cell.findParent('tfoot', false, true)){
6058 if(cell.findParent('thead', false, true)){
6060 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6061 cell = Roo.get(t).findParent('th', false, true);
6063 Roo.log("failed to find th in thead?");
6064 Roo.log(e.getTarget());
6069 var cellIndex = cell.dom.cellIndex;
6071 var ename = name == 'touchstart' ? 'click' : name;
6072 this.fireEvent("header" + ename, this, cellIndex, e);
6077 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6078 cell = Roo.get(t).findParent('td', false, true);
6080 Roo.log("failed to find th in tbody?");
6081 Roo.log(e.getTarget());
6086 var row = cell.findParent('tr', false, true);
6087 var cellIndex = cell.dom.cellIndex;
6088 var rowIndex = row.dom.rowIndex - 1;
6092 this.fireEvent("row" + name, this, rowIndex, e);
6096 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6102 onMouseover : function(e, el)
6104 var cell = Roo.get(el);
6110 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6111 cell = cell.findParent('td', false, true);
6114 var row = cell.findParent('tr', false, true);
6115 var cellIndex = cell.dom.cellIndex;
6116 var rowIndex = row.dom.rowIndex - 1; // start from 0
6118 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6122 onMouseout : function(e, el)
6124 var cell = Roo.get(el);
6130 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6131 cell = cell.findParent('td', false, true);
6134 var row = cell.findParent('tr', false, true);
6135 var cellIndex = cell.dom.cellIndex;
6136 var rowIndex = row.dom.rowIndex - 1; // start from 0
6138 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6142 onClick : function(e, el)
6144 var cell = Roo.get(el);
6146 if(!cell || (!this.cellSelection && !this.rowSelection)){
6150 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6151 cell = cell.findParent('td', false, true);
6154 if(!cell || typeof(cell) == 'undefined'){
6158 var row = cell.findParent('tr', false, true);
6160 if(!row || typeof(row) == 'undefined'){
6164 var cellIndex = cell.dom.cellIndex;
6165 var rowIndex = this.getRowIndex(row);
6167 // why??? - should these not be based on SelectionModel?
6168 if(this.cellSelection){
6169 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6172 if(this.rowSelection){
6173 this.fireEvent('rowclick', this, row, rowIndex, e);
6179 onDblClick : function(e,el)
6181 var cell = Roo.get(el);
6183 if(!cell || (!this.cellSelection && !this.rowSelection)){
6187 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6188 cell = cell.findParent('td', false, true);
6191 if(!cell || typeof(cell) == 'undefined'){
6195 var row = cell.findParent('tr', false, true);
6197 if(!row || typeof(row) == 'undefined'){
6201 var cellIndex = cell.dom.cellIndex;
6202 var rowIndex = this.getRowIndex(row);
6204 if(this.cellSelection){
6205 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6208 if(this.rowSelection){
6209 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6213 sort : function(e,el)
6215 var col = Roo.get(el);
6217 if(!col.hasClass('sortable')){
6221 var sort = col.attr('sort');
6224 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6228 this.store.sortInfo = {field : sort, direction : dir};
6231 Roo.log("calling footer first");
6232 this.footer.onClick('first');
6235 this.store.load({ params : { start : 0 } });
6239 renderHeader : function()
6247 this.totalWidth = 0;
6249 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6251 var config = cm.config[i];
6256 html: cm.getColumnHeader(i)
6261 if(typeof(config.sortable) != 'undefined' && config.sortable){
6263 c.html = '<i class="glyphicon"></i>' + c.html;
6266 if(typeof(config.lgHeader) != 'undefined'){
6267 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6270 if(typeof(config.mdHeader) != 'undefined'){
6271 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6274 if(typeof(config.smHeader) != 'undefined'){
6275 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6278 if(typeof(config.xsHeader) != 'undefined'){
6279 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6286 if(typeof(config.tooltip) != 'undefined'){
6287 c.tooltip = config.tooltip;
6290 if(typeof(config.colspan) != 'undefined'){
6291 c.colspan = config.colspan;
6294 if(typeof(config.hidden) != 'undefined' && config.hidden){
6295 c.style += ' display:none;';
6298 if(typeof(config.dataIndex) != 'undefined'){
6299 c.sort = config.dataIndex;
6304 if(typeof(config.align) != 'undefined' && config.align.length){
6305 c.style += ' text-align:' + config.align + ';';
6308 if(typeof(config.width) != 'undefined'){
6309 c.style += ' width:' + config.width + 'px;';
6310 this.totalWidth += config.width;
6312 this.totalWidth += 100; // assume minimum of 100 per column?
6315 if(typeof(config.cls) != 'undefined'){
6316 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6319 ['xs','sm','md','lg'].map(function(size){
6321 if(typeof(config[size]) == 'undefined'){
6325 if (!config[size]) { // 0 = hidden
6326 c.cls += ' hidden-' + size;
6330 c.cls += ' col-' + size + '-' + config[size];
6340 renderBody : function()
6350 colspan : this.cm.getColumnCount()
6360 renderFooter : function()
6370 colspan : this.cm.getColumnCount()
6384 // Roo.log('ds onload');
6389 var ds = this.store;
6391 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6392 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6393 if (_this.store.sortInfo) {
6395 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6396 e.select('i', true).addClass(['glyphicon-arrow-up']);
6399 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6400 e.select('i', true).addClass(['glyphicon-arrow-down']);
6405 var tbody = this.mainBody;
6407 if(ds.getCount() > 0){
6408 ds.data.each(function(d,rowIndex){
6409 var row = this.renderRow(cm, ds, rowIndex);
6411 tbody.createChild(row);
6415 if(row.cellObjects.length){
6416 Roo.each(row.cellObjects, function(r){
6417 _this.renderCellObject(r);
6424 Roo.each(this.el.select('tbody td', true).elements, function(e){
6425 e.on('mouseover', _this.onMouseover, _this);
6428 Roo.each(this.el.select('tbody td', true).elements, function(e){
6429 e.on('mouseout', _this.onMouseout, _this);
6431 this.fireEvent('rowsrendered', this);
6432 //if(this.loadMask){
6433 // this.maskEl.hide();
6440 onUpdate : function(ds,record)
6442 this.refreshRow(record);
6446 onRemove : function(ds, record, index, isUpdate){
6447 if(isUpdate !== true){
6448 this.fireEvent("beforerowremoved", this, index, record);
6450 var bt = this.mainBody.dom;
6452 var rows = this.el.select('tbody > tr', true).elements;
6454 if(typeof(rows[index]) != 'undefined'){
6455 bt.removeChild(rows[index].dom);
6458 // if(bt.rows[index]){
6459 // bt.removeChild(bt.rows[index]);
6462 if(isUpdate !== true){
6463 //this.stripeRows(index);
6464 //this.syncRowHeights(index, index);
6466 this.fireEvent("rowremoved", this, index, record);
6470 onAdd : function(ds, records, rowIndex)
6472 //Roo.log('on Add called');
6473 // - note this does not handle multiple adding very well..
6474 var bt = this.mainBody.dom;
6475 for (var i =0 ; i < records.length;i++) {
6476 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6477 //Roo.log(records[i]);
6478 //Roo.log(this.store.getAt(rowIndex+i));
6479 this.insertRow(this.store, rowIndex + i, false);
6486 refreshRow : function(record){
6487 var ds = this.store, index;
6488 if(typeof record == 'number'){
6490 record = ds.getAt(index);
6492 index = ds.indexOf(record);
6494 this.insertRow(ds, index, true);
6496 this.onRemove(ds, record, index+1, true);
6498 //this.syncRowHeights(index, index);
6500 this.fireEvent("rowupdated", this, index, record);
6503 insertRow : function(dm, rowIndex, isUpdate){
6506 this.fireEvent("beforerowsinserted", this, rowIndex);
6508 //var s = this.getScrollState();
6509 var row = this.renderRow(this.cm, this.store, rowIndex);
6510 // insert before rowIndex..
6511 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6515 if(row.cellObjects.length){
6516 Roo.each(row.cellObjects, function(r){
6517 _this.renderCellObject(r);
6522 this.fireEvent("rowsinserted", this, rowIndex);
6523 //this.syncRowHeights(firstRow, lastRow);
6524 //this.stripeRows(firstRow);
6531 getRowDom : function(rowIndex)
6533 var rows = this.el.select('tbody > tr', true).elements;
6535 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6538 // returns the object tree for a tr..
6541 renderRow : function(cm, ds, rowIndex)
6544 var d = ds.getAt(rowIndex);
6551 var cellObjects = [];
6553 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6554 var config = cm.config[i];
6556 var renderer = cm.getRenderer(i);
6560 if(typeof(renderer) !== 'undefined'){
6561 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6563 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6564 // and are rendered into the cells after the row is rendered - using the id for the element.
6566 if(typeof(value) === 'object'){
6576 rowIndex : rowIndex,
6581 this.fireEvent('rowclass', this, rowcfg);
6585 cls : rowcfg.rowClass,
6587 html: (typeof(value) === 'object') ? '' : value
6594 if(typeof(config.colspan) != 'undefined'){
6595 td.colspan = config.colspan;
6598 if(typeof(config.hidden) != 'undefined' && config.hidden){
6599 td.style += ' display:none;';
6602 if(typeof(config.align) != 'undefined' && config.align.length){
6603 td.style += ' text-align:' + config.align + ';';
6606 if(typeof(config.width) != 'undefined'){
6607 td.style += ' width:' + config.width + 'px;';
6610 if(typeof(config.cursor) != 'undefined'){
6611 td.style += ' cursor:' + config.cursor + ';';
6614 if(typeof(config.cls) != 'undefined'){
6615 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6618 ['xs','sm','md','lg'].map(function(size){
6620 if(typeof(config[size]) == 'undefined'){
6624 if (!config[size]) { // 0 = hidden
6625 td.cls += ' hidden-' + size;
6629 td.cls += ' col-' + size + '-' + config[size];
6637 row.cellObjects = cellObjects;
6645 onBeforeLoad : function()
6647 //Roo.log('ds onBeforeLoad');
6651 //if(this.loadMask){
6652 // this.maskEl.show();
6660 this.el.select('tbody', true).first().dom.innerHTML = '';
6663 * Show or hide a row.
6664 * @param {Number} rowIndex to show or hide
6665 * @param {Boolean} state hide
6667 setRowVisibility : function(rowIndex, state)
6669 var bt = this.mainBody.dom;
6671 var rows = this.el.select('tbody > tr', true).elements;
6673 if(typeof(rows[rowIndex]) == 'undefined'){
6676 rows[rowIndex].dom.style.display = state ? '' : 'none';
6680 getSelectionModel : function(){
6682 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6684 return this.selModel;
6687 * Render the Roo.bootstrap object from renderder
6689 renderCellObject : function(r)
6693 var t = r.cfg.render(r.container);
6696 Roo.each(r.cfg.cn, function(c){
6698 container: t.getChildContainer(),
6701 _this.renderCellObject(child);
6706 getRowIndex : function(row)
6710 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6721 * Returns the grid's underlying element = used by panel.Grid
6722 * @return {Element} The element
6724 getGridEl : function(){
6728 * Forces a resize - used by panel.Grid
6729 * @return {Element} The element
6731 autoSize : function()
6733 //var ctr = Roo.get(this.container.dom.parentElement);
6734 var ctr = Roo.get(this.el.dom);
6736 var thd = this.getGridEl().select('thead',true).first();
6737 var tbd = this.getGridEl().select('tbody', true).first();
6738 var tfd = this.getGridEl().select('tfoot', true).first();
6740 var cw = ctr.getWidth();
6744 tbd.setSize(ctr.getWidth(),
6745 ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6747 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6750 cw = Math.max(cw, this.totalWidth);
6751 this.getGridEl().select('tr',true).setWidth(cw);
6752 // resize 'expandable coloumn?
6754 return; // we doe not have a view in this design..
6757 onBodyScroll: function()
6760 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6761 this.mainHead.setStyle({
6762 'position' : 'relative',
6763 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6780 * @class Roo.bootstrap.TableCell
6781 * @extends Roo.bootstrap.Component
6782 * Bootstrap TableCell class
6783 * @cfg {String} html cell contain text
6784 * @cfg {String} cls cell class
6785 * @cfg {String} tag cell tag (td|th) default td
6786 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6787 * @cfg {String} align Aligns the content in a cell
6788 * @cfg {String} axis Categorizes cells
6789 * @cfg {String} bgcolor Specifies the background color of a cell
6790 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6791 * @cfg {Number} colspan Specifies the number of columns a cell should span
6792 * @cfg {String} headers Specifies one or more header cells a cell is related to
6793 * @cfg {Number} height Sets the height of a cell
6794 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6795 * @cfg {Number} rowspan Sets the number of rows a cell should span
6796 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6797 * @cfg {String} valign Vertical aligns the content in a cell
6798 * @cfg {Number} width Specifies the width of a cell
6801 * Create a new TableCell
6802 * @param {Object} config The config object
6805 Roo.bootstrap.TableCell = function(config){
6806 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6809 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6829 getAutoCreate : function(){
6830 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6850 cfg.align=this.align
6856 cfg.bgcolor=this.bgcolor
6859 cfg.charoff=this.charoff
6862 cfg.colspan=this.colspan
6865 cfg.headers=this.headers
6868 cfg.height=this.height
6871 cfg.nowrap=this.nowrap
6874 cfg.rowspan=this.rowspan
6877 cfg.scope=this.scope
6880 cfg.valign=this.valign
6883 cfg.width=this.width
6902 * @class Roo.bootstrap.TableRow
6903 * @extends Roo.bootstrap.Component
6904 * Bootstrap TableRow class
6905 * @cfg {String} cls row class
6906 * @cfg {String} align Aligns the content in a table row
6907 * @cfg {String} bgcolor Specifies a background color for a table row
6908 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6909 * @cfg {String} valign Vertical aligns the content in a table row
6912 * Create a new TableRow
6913 * @param {Object} config The config object
6916 Roo.bootstrap.TableRow = function(config){
6917 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6920 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6928 getAutoCreate : function(){
6929 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6939 cfg.align = this.align;
6942 cfg.bgcolor = this.bgcolor;
6945 cfg.charoff = this.charoff;
6948 cfg.valign = this.valign;
6966 * @class Roo.bootstrap.TableBody
6967 * @extends Roo.bootstrap.Component
6968 * Bootstrap TableBody class
6969 * @cfg {String} cls element class
6970 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6971 * @cfg {String} align Aligns the content inside the element
6972 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6973 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6976 * Create a new TableBody
6977 * @param {Object} config The config object
6980 Roo.bootstrap.TableBody = function(config){
6981 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6984 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6992 getAutoCreate : function(){
6993 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7007 cfg.align = this.align;
7010 cfg.charoff = this.charoff;
7013 cfg.valign = this.valign;
7020 // initEvents : function()
7027 // this.store = Roo.factory(this.store, Roo.data);
7028 // this.store.on('load', this.onLoad, this);
7030 // this.store.load();
7034 // onLoad: function ()
7036 // this.fireEvent('load', this);
7046 * Ext JS Library 1.1.1
7047 * Copyright(c) 2006-2007, Ext JS, LLC.
7049 * Originally Released Under LGPL - original licence link has changed is not relivant.
7052 * <script type="text/javascript">
7055 // as we use this in bootstrap.
7056 Roo.namespace('Roo.form');
7058 * @class Roo.form.Action
7059 * Internal Class used to handle form actions
7061 * @param {Roo.form.BasicForm} el The form element or its id
7062 * @param {Object} config Configuration options
7067 // define the action interface
7068 Roo.form.Action = function(form, options){
7070 this.options = options || {};
7073 * Client Validation Failed
7076 Roo.form.Action.CLIENT_INVALID = 'client';
7078 * Server Validation Failed
7081 Roo.form.Action.SERVER_INVALID = 'server';
7083 * Connect to Server Failed
7086 Roo.form.Action.CONNECT_FAILURE = 'connect';
7088 * Reading Data from Server Failed
7091 Roo.form.Action.LOAD_FAILURE = 'load';
7093 Roo.form.Action.prototype = {
7095 failureType : undefined,
7096 response : undefined,
7100 run : function(options){
7105 success : function(response){
7110 handleResponse : function(response){
7114 // default connection failure
7115 failure : function(response){
7117 this.response = response;
7118 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7119 this.form.afterAction(this, false);
7122 processResponse : function(response){
7123 this.response = response;
7124 if(!response.responseText){
7127 this.result = this.handleResponse(response);
7131 // utility functions used internally
7132 getUrl : function(appendParams){
7133 var url = this.options.url || this.form.url || this.form.el.dom.action;
7135 var p = this.getParams();
7137 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7143 getMethod : function(){
7144 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7147 getParams : function(){
7148 var bp = this.form.baseParams;
7149 var p = this.options.params;
7151 if(typeof p == "object"){
7152 p = Roo.urlEncode(Roo.applyIf(p, bp));
7153 }else if(typeof p == 'string' && bp){
7154 p += '&' + Roo.urlEncode(bp);
7157 p = Roo.urlEncode(bp);
7162 createCallback : function(){
7164 success: this.success,
7165 failure: this.failure,
7167 timeout: (this.form.timeout*1000),
7168 upload: this.form.fileUpload ? this.success : undefined
7173 Roo.form.Action.Submit = function(form, options){
7174 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7177 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7180 haveProgress : false,
7181 uploadComplete : false,
7183 // uploadProgress indicator.
7184 uploadProgress : function()
7186 if (!this.form.progressUrl) {
7190 if (!this.haveProgress) {
7191 Roo.MessageBox.progress("Uploading", "Uploading");
7193 if (this.uploadComplete) {
7194 Roo.MessageBox.hide();
7198 this.haveProgress = true;
7200 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7202 var c = new Roo.data.Connection();
7204 url : this.form.progressUrl,
7209 success : function(req){
7210 //console.log(data);
7214 rdata = Roo.decode(req.responseText)
7216 Roo.log("Invalid data from server..");
7220 if (!rdata || !rdata.success) {
7222 Roo.MessageBox.alert(Roo.encode(rdata));
7225 var data = rdata.data;
7227 if (this.uploadComplete) {
7228 Roo.MessageBox.hide();
7233 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7234 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7237 this.uploadProgress.defer(2000,this);
7240 failure: function(data) {
7241 Roo.log('progress url failed ');
7252 // run get Values on the form, so it syncs any secondary forms.
7253 this.form.getValues();
7255 var o = this.options;
7256 var method = this.getMethod();
7257 var isPost = method == 'POST';
7258 if(o.clientValidation === false || this.form.isValid()){
7260 if (this.form.progressUrl) {
7261 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7262 (new Date() * 1) + '' + Math.random());
7267 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7268 form:this.form.el.dom,
7269 url:this.getUrl(!isPost),
7271 params:isPost ? this.getParams() : null,
7272 isUpload: this.form.fileUpload
7275 this.uploadProgress();
7277 }else if (o.clientValidation !== false){ // client validation failed
7278 this.failureType = Roo.form.Action.CLIENT_INVALID;
7279 this.form.afterAction(this, false);
7283 success : function(response)
7285 this.uploadComplete= true;
7286 if (this.haveProgress) {
7287 Roo.MessageBox.hide();
7291 var result = this.processResponse(response);
7292 if(result === true || result.success){
7293 this.form.afterAction(this, true);
7297 this.form.markInvalid(result.errors);
7298 this.failureType = Roo.form.Action.SERVER_INVALID;
7300 this.form.afterAction(this, false);
7302 failure : function(response)
7304 this.uploadComplete= true;
7305 if (this.haveProgress) {
7306 Roo.MessageBox.hide();
7309 this.response = response;
7310 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7311 this.form.afterAction(this, false);
7314 handleResponse : function(response){
7315 if(this.form.errorReader){
7316 var rs = this.form.errorReader.read(response);
7319 for(var i = 0, len = rs.records.length; i < len; i++) {
7320 var r = rs.records[i];
7324 if(errors.length < 1){
7328 success : rs.success,
7334 ret = Roo.decode(response.responseText);
7338 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7348 Roo.form.Action.Load = function(form, options){
7349 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7350 this.reader = this.form.reader;
7353 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7358 Roo.Ajax.request(Roo.apply(
7359 this.createCallback(), {
7360 method:this.getMethod(),
7361 url:this.getUrl(false),
7362 params:this.getParams()
7366 success : function(response){
7368 var result = this.processResponse(response);
7369 if(result === true || !result.success || !result.data){
7370 this.failureType = Roo.form.Action.LOAD_FAILURE;
7371 this.form.afterAction(this, false);
7374 this.form.clearInvalid();
7375 this.form.setValues(result.data);
7376 this.form.afterAction(this, true);
7379 handleResponse : function(response){
7380 if(this.form.reader){
7381 var rs = this.form.reader.read(response);
7382 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7384 success : rs.success,
7388 return Roo.decode(response.responseText);
7392 Roo.form.Action.ACTION_TYPES = {
7393 'load' : Roo.form.Action.Load,
7394 'submit' : Roo.form.Action.Submit
7403 * @class Roo.bootstrap.Form
7404 * @extends Roo.bootstrap.Component
7405 * Bootstrap Form class
7406 * @cfg {String} method GET | POST (default POST)
7407 * @cfg {String} labelAlign top | left (default top)
7408 * @cfg {String} align left | right - for navbars
7409 * @cfg {Boolean} loadMask load mask when submit (default true)
7414 * @param {Object} config The config object
7418 Roo.bootstrap.Form = function(config){
7419 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7422 * @event clientvalidation
7423 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7424 * @param {Form} this
7425 * @param {Boolean} valid true if the form has passed client-side validation
7427 clientvalidation: true,
7429 * @event beforeaction
7430 * Fires before any action is performed. Return false to cancel the action.
7431 * @param {Form} this
7432 * @param {Action} action The action to be performed
7436 * @event actionfailed
7437 * Fires when an action fails.
7438 * @param {Form} this
7439 * @param {Action} action The action that failed
7441 actionfailed : true,
7443 * @event actioncomplete
7444 * Fires when an action is completed.
7445 * @param {Form} this
7446 * @param {Action} action The action that completed
7448 actioncomplete : true
7453 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7456 * @cfg {String} method
7457 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7462 * The URL to use for form actions if one isn't supplied in the action options.
7465 * @cfg {Boolean} fileUpload
7466 * Set to true if this form is a file upload.
7470 * @cfg {Object} baseParams
7471 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7475 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7479 * @cfg {Sting} align (left|right) for navbar forms
7484 activeAction : null,
7487 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7488 * element by passing it or its id or mask the form itself by passing in true.
7491 waitMsgTarget : false,
7495 getAutoCreate : function(){
7499 method : this.method || 'POST',
7500 id : this.id || Roo.id(),
7503 if (this.parent().xtype.match(/^Nav/)) {
7504 cfg.cls = 'navbar-form navbar-' + this.align;
7508 if (this.labelAlign == 'left' ) {
7509 cfg.cls += ' form-horizontal';
7515 initEvents : function()
7517 this.el.on('submit', this.onSubmit, this);
7518 // this was added as random key presses on the form where triggering form submit.
7519 this.el.on('keypress', function(e) {
7520 if (e.getCharCode() != 13) {
7523 // we might need to allow it for textareas.. and some other items.
7524 // check e.getTarget().
7526 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7530 Roo.log("keypress blocked");
7538 onSubmit : function(e){
7543 * Returns true if client-side validation on the form is successful.
7546 isValid : function(){
7547 var items = this.getItems();
7549 items.each(function(f){
7558 * Returns true if any fields in this form have changed since their original load.
7561 isDirty : function(){
7563 var items = this.getItems();
7564 items.each(function(f){
7574 * Performs a predefined action (submit or load) or custom actions you define on this form.
7575 * @param {String} actionName The name of the action type
7576 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7577 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7578 * accept other config options):
7580 Property Type Description
7581 ---------------- --------------- ----------------------------------------------------------------------------------
7582 url String The url for the action (defaults to the form's url)
7583 method String The form method to use (defaults to the form's method, or POST if not defined)
7584 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7585 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7586 validate the form on the client (defaults to false)
7588 * @return {BasicForm} this
7590 doAction : function(action, options){
7591 if(typeof action == 'string'){
7592 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7594 if(this.fireEvent('beforeaction', this, action) !== false){
7595 this.beforeAction(action);
7596 action.run.defer(100, action);
7602 beforeAction : function(action){
7603 var o = action.options;
7606 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7608 // not really supported yet.. ??
7610 //if(this.waitMsgTarget === true){
7611 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7612 //}else if(this.waitMsgTarget){
7613 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7614 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7616 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7622 afterAction : function(action, success){
7623 this.activeAction = null;
7624 var o = action.options;
7626 //if(this.waitMsgTarget === true){
7628 //}else if(this.waitMsgTarget){
7629 // this.waitMsgTarget.unmask();
7631 // Roo.MessageBox.updateProgress(1);
7632 // Roo.MessageBox.hide();
7639 Roo.callback(o.success, o.scope, [this, action]);
7640 this.fireEvent('actioncomplete', this, action);
7644 // failure condition..
7645 // we have a scenario where updates need confirming.
7646 // eg. if a locking scenario exists..
7647 // we look for { errors : { needs_confirm : true }} in the response.
7649 (typeof(action.result) != 'undefined') &&
7650 (typeof(action.result.errors) != 'undefined') &&
7651 (typeof(action.result.errors.needs_confirm) != 'undefined')
7654 Roo.log("not supported yet");
7657 Roo.MessageBox.confirm(
7658 "Change requires confirmation",
7659 action.result.errorMsg,
7664 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7674 Roo.callback(o.failure, o.scope, [this, action]);
7675 // show an error message if no failed handler is set..
7676 if (!this.hasListener('actionfailed')) {
7677 Roo.log("need to add dialog support");
7679 Roo.MessageBox.alert("Error",
7680 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7681 action.result.errorMsg :
7682 "Saving Failed, please check your entries or try again"
7687 this.fireEvent('actionfailed', this, action);
7692 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7693 * @param {String} id The value to search for
7696 findField : function(id){
7697 var items = this.getItems();
7698 var field = items.get(id);
7700 items.each(function(f){
7701 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7708 return field || null;
7711 * Mark fields in this form invalid in bulk.
7712 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7713 * @return {BasicForm} this
7715 markInvalid : function(errors){
7716 if(errors instanceof Array){
7717 for(var i = 0, len = errors.length; i < len; i++){
7718 var fieldError = errors[i];
7719 var f = this.findField(fieldError.id);
7721 f.markInvalid(fieldError.msg);
7727 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7728 field.markInvalid(errors[id]);
7732 //Roo.each(this.childForms || [], function (f) {
7733 // f.markInvalid(errors);
7740 * Set values for fields in this form in bulk.
7741 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7742 * @return {BasicForm} this
7744 setValues : function(values){
7745 if(values instanceof Array){ // array of objects
7746 for(var i = 0, len = values.length; i < len; i++){
7748 var f = this.findField(v.id);
7750 f.setValue(v.value);
7751 if(this.trackResetOnLoad){
7752 f.originalValue = f.getValue();
7756 }else{ // object hash
7759 if(typeof values[id] != 'function' && (field = this.findField(id))){
7761 if (field.setFromData &&
7763 field.displayField &&
7764 // combos' with local stores can
7765 // be queried via setValue()
7766 // to set their value..
7767 (field.store && !field.store.isLocal)
7771 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7772 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7773 field.setFromData(sd);
7776 field.setValue(values[id]);
7780 if(this.trackResetOnLoad){
7781 field.originalValue = field.getValue();
7787 //Roo.each(this.childForms || [], function (f) {
7788 // f.setValues(values);
7795 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7796 * they are returned as an array.
7797 * @param {Boolean} asString
7800 getValues : function(asString){
7801 //if (this.childForms) {
7802 // copy values from the child forms
7803 // Roo.each(this.childForms, function (f) {
7804 // this.setValues(f.getValues());
7810 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7811 if(asString === true){
7814 return Roo.urlDecode(fs);
7818 * Returns the fields in this form as an object with key/value pairs.
7819 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7822 getFieldValues : function(with_hidden)
7824 var items = this.getItems();
7826 items.each(function(f){
7830 var v = f.getValue();
7831 if (f.inputType =='radio') {
7832 if (typeof(ret[f.getName()]) == 'undefined') {
7833 ret[f.getName()] = ''; // empty..
7836 if (!f.el.dom.checked) {
7844 // not sure if this supported any more..
7845 if ((typeof(v) == 'object') && f.getRawValue) {
7846 v = f.getRawValue() ; // dates..
7848 // combo boxes where name != hiddenName...
7849 if (f.name != f.getName()) {
7850 ret[f.name] = f.getRawValue();
7852 ret[f.getName()] = v;
7859 * Clears all invalid messages in this form.
7860 * @return {BasicForm} this
7862 clearInvalid : function(){
7863 var items = this.getItems();
7865 items.each(function(f){
7876 * @return {BasicForm} this
7879 var items = this.getItems();
7880 items.each(function(f){
7884 Roo.each(this.childForms || [], function (f) {
7891 getItems : function()
7893 var r=new Roo.util.MixedCollection(false, function(o){
7894 return o.id || (o.id = Roo.id());
7896 var iter = function(el) {
7903 Roo.each(el.items,function(e) {
7923 * Ext JS Library 1.1.1
7924 * Copyright(c) 2006-2007, Ext JS, LLC.
7926 * Originally Released Under LGPL - original licence link has changed is not relivant.
7929 * <script type="text/javascript">
7932 * @class Roo.form.VTypes
7933 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7936 Roo.form.VTypes = function(){
7937 // closure these in so they are only created once.
7938 var alpha = /^[a-zA-Z_]+$/;
7939 var alphanum = /^[a-zA-Z0-9_]+$/;
7940 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7941 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7943 // All these messages and functions are configurable
7946 * The function used to validate email addresses
7947 * @param {String} value The email address
7949 'email' : function(v){
7950 return email.test(v);
7953 * The error text to display when the email validation function returns false
7956 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7958 * The keystroke filter mask to be applied on email input
7961 'emailMask' : /[a-z0-9_\.\-@]/i,
7964 * The function used to validate URLs
7965 * @param {String} value The URL
7967 'url' : function(v){
7971 * The error text to display when the url validation function returns false
7974 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7977 * The function used to validate alpha values
7978 * @param {String} value The value
7980 'alpha' : function(v){
7981 return alpha.test(v);
7984 * The error text to display when the alpha validation function returns false
7987 'alphaText' : 'This field should only contain letters and _',
7989 * The keystroke filter mask to be applied on alpha input
7992 'alphaMask' : /[a-z_]/i,
7995 * The function used to validate alphanumeric values
7996 * @param {String} value The value
7998 'alphanum' : function(v){
7999 return alphanum.test(v);
8002 * The error text to display when the alphanumeric validation function returns false
8005 'alphanumText' : 'This field should only contain letters, numbers and _',
8007 * The keystroke filter mask to be applied on alphanumeric input
8010 'alphanumMask' : /[a-z0-9_]/i
8020 * @class Roo.bootstrap.Input
8021 * @extends Roo.bootstrap.Component
8022 * Bootstrap Input class
8023 * @cfg {Boolean} disabled is it disabled
8024 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8025 * @cfg {String} name name of the input
8026 * @cfg {string} fieldLabel - the label associated
8027 * @cfg {string} placeholder - placeholder to put in text.
8028 * @cfg {string} before - input group add on before
8029 * @cfg {string} after - input group add on after
8030 * @cfg {string} size - (lg|sm) or leave empty..
8031 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8032 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8033 * @cfg {Number} md colspan out of 12 for computer-sized screens
8034 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8035 * @cfg {string} value default value of the input
8036 * @cfg {Number} labelWidth set the width of label (0-12)
8037 * @cfg {String} labelAlign (top|left)
8038 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8039 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8040 * @cfg {String} indicatorpos (left|right) default left
8042 * @cfg {String} align (left|center|right) Default left
8043 * @cfg {Boolean} forceFeedback (true|false) Default false
8049 * Create a new Input
8050 * @param {Object} config The config object
8053 Roo.bootstrap.Input = function(config){
8054 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8059 * Fires when this field receives input focus.
8060 * @param {Roo.form.Field} this
8065 * Fires when this field loses input focus.
8066 * @param {Roo.form.Field} this
8071 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8072 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8073 * @param {Roo.form.Field} this
8074 * @param {Roo.EventObject} e The event object
8079 * Fires just before the field blurs if the field value has changed.
8080 * @param {Roo.form.Field} this
8081 * @param {Mixed} newValue The new value
8082 * @param {Mixed} oldValue The original value
8087 * Fires after the field has been marked as invalid.
8088 * @param {Roo.form.Field} this
8089 * @param {String} msg The validation message
8094 * Fires after the field has been validated with no errors.
8095 * @param {Roo.form.Field} this
8100 * Fires after the key up
8101 * @param {Roo.form.Field} this
8102 * @param {Roo.EventObject} e The event Object
8108 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8110 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8111 automatic validation (defaults to "keyup").
8113 validationEvent : "keyup",
8115 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8117 validateOnBlur : true,
8119 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8121 validationDelay : 250,
8123 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8125 focusClass : "x-form-focus", // not needed???
8129 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8131 invalidClass : "has-warning",
8134 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8136 validClass : "has-success",
8139 * @cfg {Boolean} hasFeedback (true|false) default true
8144 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8146 invalidFeedbackClass : "glyphicon-warning-sign",
8149 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8151 validFeedbackClass : "glyphicon-ok",
8154 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8156 selectOnFocus : false,
8159 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8163 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8168 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8170 disableKeyFilter : false,
8173 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8177 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8181 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8183 blankText : "This field is required",
8186 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8190 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8192 maxLength : Number.MAX_VALUE,
8194 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8196 minLengthText : "The minimum length for this field is {0}",
8198 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8200 maxLengthText : "The maximum length for this field is {0}",
8204 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8205 * If available, this function will be called only after the basic validators all return true, and will be passed the
8206 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8210 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8211 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8212 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8216 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8220 autocomplete: false,
8239 formatedValue : false,
8240 forceFeedback : false,
8242 indicatorpos : 'left',
8244 parentLabelAlign : function()
8247 while (parent.parent()) {
8248 parent = parent.parent();
8249 if (typeof(parent.labelAlign) !='undefined') {
8250 return parent.labelAlign;
8257 getAutoCreate : function()
8259 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8265 if(this.inputType != 'hidden'){
8266 cfg.cls = 'form-group' //input-group
8272 type : this.inputType,
8274 cls : 'form-control',
8275 placeholder : this.placeholder || '',
8276 autocomplete : this.autocomplete || 'new-password'
8280 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8283 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8284 input.maxLength = this.maxLength;
8287 if (this.disabled) {
8288 input.disabled=true;
8291 if (this.readOnly) {
8292 input.readonly=true;
8296 input.name = this.name;
8300 input.cls += ' input-' + this.size;
8304 ['xs','sm','md','lg'].map(function(size){
8305 if (settings[size]) {
8306 cfg.cls += ' col-' + size + '-' + settings[size];
8310 var inputblock = input;
8314 cls: 'glyphicon form-control-feedback'
8317 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8320 cls : 'has-feedback',
8328 if (this.before || this.after) {
8331 cls : 'input-group',
8335 if (this.before && typeof(this.before) == 'string') {
8337 inputblock.cn.push({
8339 cls : 'roo-input-before input-group-addon',
8343 if (this.before && typeof(this.before) == 'object') {
8344 this.before = Roo.factory(this.before);
8346 inputblock.cn.push({
8348 cls : 'roo-input-before input-group-' +
8349 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8353 inputblock.cn.push(input);
8355 if (this.after && typeof(this.after) == 'string') {
8356 inputblock.cn.push({
8358 cls : 'roo-input-after input-group-addon',
8362 if (this.after && typeof(this.after) == 'object') {
8363 this.after = Roo.factory(this.after);
8365 inputblock.cn.push({
8367 cls : 'roo-input-after input-group-' +
8368 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8372 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8373 inputblock.cls += ' has-feedback';
8374 inputblock.cn.push(feedback);
8378 if (align ==='left' && this.fieldLabel.length) {
8383 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8384 tooltip : 'This field is required'
8389 cls : 'control-label col-sm-' + this.labelWidth,
8390 html : this.fieldLabel
8394 cls : "col-sm-" + (12 - this.labelWidth),
8402 if(this.indicatorpos == 'right'){
8407 cls : 'control-label col-sm-' + this.labelWidth,
8408 html : this.fieldLabel
8413 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8414 tooltip : 'This field is required'
8417 cls : "col-sm-" + (12 - this.labelWidth),
8426 } else if ( this.fieldLabel.length) {
8431 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8432 tooltip : 'This field is required'
8436 //cls : 'input-group-addon',
8437 html : this.fieldLabel
8445 if(this.indicatorpos == 'right'){
8450 //cls : 'input-group-addon',
8451 html : this.fieldLabel
8456 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8457 tooltip : 'This field is required'
8477 if (this.parentType === 'Navbar' && this.parent().bar) {
8478 cfg.cls += ' navbar-form';
8481 if (this.parentType === 'NavGroup') {
8482 cfg.cls += ' navbar-form';
8490 * return the real input element.
8492 inputEl: function ()
8494 return this.el.select('input.form-control',true).first();
8497 tooltipEl : function()
8499 return this.inputEl();
8502 indicatorEl : function()
8504 var indicator = this.el.select('i.roo-required-indicator',true).first();
8514 setDisabled : function(v)
8516 var i = this.inputEl().dom;
8518 i.removeAttribute('disabled');
8522 i.setAttribute('disabled','true');
8524 initEvents : function()
8527 this.inputEl().on("keydown" , this.fireKey, this);
8528 this.inputEl().on("focus", this.onFocus, this);
8529 this.inputEl().on("blur", this.onBlur, this);
8531 this.inputEl().relayEvent('keyup', this);
8533 this.indicator = this.indicatorEl();
8536 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8537 this.indicator.hide();
8540 // reference to original value for reset
8541 this.originalValue = this.getValue();
8542 //Roo.form.TextField.superclass.initEvents.call(this);
8543 if(this.validationEvent == 'keyup'){
8544 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8545 this.inputEl().on('keyup', this.filterValidation, this);
8547 else if(this.validationEvent !== false){
8548 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8551 if(this.selectOnFocus){
8552 this.on("focus", this.preFocus, this);
8555 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8556 this.inputEl().on("keypress", this.filterKeys, this);
8558 this.inputEl().relayEvent('keypress', this);
8561 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8562 this.el.on("click", this.autoSize, this);
8565 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8566 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8569 if (typeof(this.before) == 'object') {
8570 this.before.render(this.el.select('.roo-input-before',true).first());
8572 if (typeof(this.after) == 'object') {
8573 this.after.render(this.el.select('.roo-input-after',true).first());
8578 filterValidation : function(e){
8579 if(!e.isNavKeyPress()){
8580 this.validationTask.delay(this.validationDelay);
8584 * Validates the field value
8585 * @return {Boolean} True if the value is valid, else false
8587 validate : function(){
8588 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8589 if(this.disabled || this.validateValue(this.getRawValue())){
8600 * Validates a value according to the field's validation rules and marks the field as invalid
8601 * if the validation fails
8602 * @param {Mixed} value The value to validate
8603 * @return {Boolean} True if the value is valid, else false
8605 validateValue : function(value){
8606 if(value.length < 1) { // if it's blank
8607 if(this.allowBlank){
8613 if(value.length < this.minLength){
8616 if(value.length > this.maxLength){
8620 var vt = Roo.form.VTypes;
8621 if(!vt[this.vtype](value, this)){
8625 if(typeof this.validator == "function"){
8626 var msg = this.validator(value);
8632 if(this.regex && !this.regex.test(value)){
8642 fireKey : function(e){
8643 //Roo.log('field ' + e.getKey());
8644 if(e.isNavKeyPress()){
8645 this.fireEvent("specialkey", this, e);
8648 focus : function (selectText){
8650 this.inputEl().focus();
8651 if(selectText === true){
8652 this.inputEl().dom.select();
8658 onFocus : function(){
8659 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8660 // this.el.addClass(this.focusClass);
8663 this.hasFocus = true;
8664 this.startValue = this.getValue();
8665 this.fireEvent("focus", this);
8669 beforeBlur : Roo.emptyFn,
8673 onBlur : function(){
8675 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8676 //this.el.removeClass(this.focusClass);
8678 this.hasFocus = false;
8679 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8682 var v = this.getValue();
8683 if(String(v) !== String(this.startValue)){
8684 this.fireEvent('change', this, v, this.startValue);
8686 this.fireEvent("blur", this);
8690 * Resets the current field value to the originally loaded value and clears any validation messages
8693 this.setValue(this.originalValue);
8697 * Returns the name of the field
8698 * @return {Mixed} name The name field
8700 getName: function(){
8704 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8705 * @return {Mixed} value The field value
8707 getValue : function(){
8709 var v = this.inputEl().getValue();
8714 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8715 * @return {Mixed} value The field value
8717 getRawValue : function(){
8718 var v = this.inputEl().getValue();
8724 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8725 * @param {Mixed} value The value to set
8727 setRawValue : function(v){
8728 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8731 selectText : function(start, end){
8732 var v = this.getRawValue();
8734 start = start === undefined ? 0 : start;
8735 end = end === undefined ? v.length : end;
8736 var d = this.inputEl().dom;
8737 if(d.setSelectionRange){
8738 d.setSelectionRange(start, end);
8739 }else if(d.createTextRange){
8740 var range = d.createTextRange();
8741 range.moveStart("character", start);
8742 range.moveEnd("character", v.length-end);
8749 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8750 * @param {Mixed} value The value to set
8752 setValue : function(v){
8755 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8761 processValue : function(value){
8762 if(this.stripCharsRe){
8763 var newValue = value.replace(this.stripCharsRe, '');
8764 if(newValue !== value){
8765 this.setRawValue(newValue);
8772 preFocus : function(){
8774 if(this.selectOnFocus){
8775 this.inputEl().dom.select();
8778 filterKeys : function(e){
8780 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8783 var c = e.getCharCode(), cc = String.fromCharCode(c);
8784 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8787 if(!this.maskRe.test(cc)){
8792 * Clear any invalid styles/messages for this field
8794 clearInvalid : function(){
8796 if(!this.el || this.preventMark){ // not rendered
8801 this.indicator.hide();
8804 this.el.removeClass(this.invalidClass);
8806 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8808 var feedback = this.el.select('.form-control-feedback', true).first();
8811 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8816 this.fireEvent('valid', this);
8820 * Mark this field as valid
8822 markValid : function()
8824 if(!this.el || this.preventMark){ // not rendered
8828 this.el.removeClass([this.invalidClass, this.validClass]);
8830 var feedback = this.el.select('.form-control-feedback', true).first();
8833 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8836 if(this.disabled || this.allowBlank){
8841 this.indicator.hide();
8844 this.el.addClass(this.validClass);
8846 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8848 var feedback = this.el.select('.form-control-feedback', true).first();
8851 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8852 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8857 this.fireEvent('valid', this);
8861 * Mark this field as invalid
8862 * @param {String} msg The validation message
8864 markInvalid : function(msg)
8866 if(!this.el || this.preventMark){ // not rendered
8870 this.el.removeClass([this.invalidClass, this.validClass]);
8872 var feedback = this.el.select('.form-control-feedback', true).first();
8875 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8878 if(this.disabled || this.allowBlank){
8883 this.indicator.show();
8886 this.el.addClass(this.invalidClass);
8888 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8890 var feedback = this.el.select('.form-control-feedback', true).first();
8893 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8895 if(this.getValue().length || this.forceFeedback){
8896 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8903 this.fireEvent('invalid', this, msg);
8906 SafariOnKeyDown : function(event)
8908 // this is a workaround for a password hang bug on chrome/ webkit.
8910 var isSelectAll = false;
8912 if(this.inputEl().dom.selectionEnd > 0){
8913 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8915 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8916 event.preventDefault();
8921 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8923 event.preventDefault();
8924 // this is very hacky as keydown always get's upper case.
8926 var cc = String.fromCharCode(event.getCharCode());
8927 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8931 adjustWidth : function(tag, w){
8932 tag = tag.toLowerCase();
8933 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8934 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8938 if(tag == 'textarea'){
8941 }else if(Roo.isOpera){
8945 if(tag == 'textarea'){
8964 * @class Roo.bootstrap.TextArea
8965 * @extends Roo.bootstrap.Input
8966 * Bootstrap TextArea class
8967 * @cfg {Number} cols Specifies the visible width of a text area
8968 * @cfg {Number} rows Specifies the visible number of lines in a text area
8969 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8970 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8971 * @cfg {string} html text
8974 * Create a new TextArea
8975 * @param {Object} config The config object
8978 Roo.bootstrap.TextArea = function(config){
8979 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8983 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8993 getAutoCreate : function(){
8995 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9006 value : this.value || '',
9007 html: this.html || '',
9008 cls : 'form-control',
9009 placeholder : this.placeholder || ''
9013 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9014 input.maxLength = this.maxLength;
9018 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9022 input.cols = this.cols;
9025 if (this.readOnly) {
9026 input.readonly = true;
9030 input.name = this.name;
9034 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9038 ['xs','sm','md','lg'].map(function(size){
9039 if (settings[size]) {
9040 cfg.cls += ' col-' + size + '-' + settings[size];
9044 var inputblock = input;
9046 if(this.hasFeedback && !this.allowBlank){
9050 cls: 'glyphicon form-control-feedback'
9054 cls : 'has-feedback',
9063 if (this.before || this.after) {
9066 cls : 'input-group',
9070 inputblock.cn.push({
9072 cls : 'input-group-addon',
9077 inputblock.cn.push(input);
9079 if(this.hasFeedback && !this.allowBlank){
9080 inputblock.cls += ' has-feedback';
9081 inputblock.cn.push(feedback);
9085 inputblock.cn.push({
9087 cls : 'input-group-addon',
9094 if (align ==='left' && this.fieldLabel.length) {
9095 // Roo.log("left and has label");
9101 cls : 'control-label col-sm-' + this.labelWidth,
9102 html : this.fieldLabel
9106 cls : "col-sm-" + (12 - this.labelWidth),
9113 } else if ( this.fieldLabel.length) {
9114 // Roo.log(" label");
9119 //cls : 'input-group-addon',
9120 html : this.fieldLabel
9130 // Roo.log(" no label && no align");
9140 if (this.disabled) {
9141 input.disabled=true;
9148 * return the real textarea element.
9150 inputEl: function ()
9152 return this.el.select('textarea.form-control',true).first();
9156 * Clear any invalid styles/messages for this field
9158 clearInvalid : function()
9161 if(!this.el || this.preventMark){ // not rendered
9165 var label = this.el.select('label', true).first();
9166 var icon = this.el.select('i.fa-star', true).first();
9172 this.el.removeClass(this.invalidClass);
9174 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9176 var feedback = this.el.select('.form-control-feedback', true).first();
9179 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9184 this.fireEvent('valid', this);
9188 * Mark this field as valid
9190 markValid : function()
9192 if(!this.el || this.preventMark){ // not rendered
9196 this.el.removeClass([this.invalidClass, this.validClass]);
9198 var feedback = this.el.select('.form-control-feedback', true).first();
9201 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9204 if(this.disabled || this.allowBlank){
9208 var label = this.el.select('label', true).first();
9209 var icon = this.el.select('i.fa-star', true).first();
9215 this.el.addClass(this.validClass);
9217 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9219 var feedback = this.el.select('.form-control-feedback', true).first();
9222 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9223 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9228 this.fireEvent('valid', this);
9232 * Mark this field as invalid
9233 * @param {String} msg The validation message
9235 markInvalid : function(msg)
9237 if(!this.el || this.preventMark){ // not rendered
9241 this.el.removeClass([this.invalidClass, this.validClass]);
9243 var feedback = this.el.select('.form-control-feedback', true).first();
9246 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9249 if(this.disabled || this.allowBlank){
9253 var label = this.el.select('label', true).first();
9254 var icon = this.el.select('i.fa-star', true).first();
9256 if(!this.getValue().length && label && !icon){
9257 this.el.createChild({
9259 cls : 'text-danger fa fa-lg fa-star',
9260 tooltip : 'This field is required',
9261 style : 'margin-right:5px;'
9265 this.el.addClass(this.invalidClass);
9267 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9269 var feedback = this.el.select('.form-control-feedback', true).first();
9272 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9274 if(this.getValue().length || this.forceFeedback){
9275 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9282 this.fireEvent('invalid', this, msg);
9290 * trigger field - base class for combo..
9295 * @class Roo.bootstrap.TriggerField
9296 * @extends Roo.bootstrap.Input
9297 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9298 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9299 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9300 * for which you can provide a custom implementation. For example:
9302 var trigger = new Roo.bootstrap.TriggerField();
9303 trigger.onTriggerClick = myTriggerFn;
9304 trigger.applyTo('my-field');
9307 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9308 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9309 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9310 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9311 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9314 * Create a new TriggerField.
9315 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9316 * to the base TextField)
9318 Roo.bootstrap.TriggerField = function(config){
9319 this.mimicing = false;
9320 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9323 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9325 * @cfg {String} triggerClass A CSS class to apply to the trigger
9328 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9333 * @cfg {Boolean} removable (true|false) special filter default false
9337 /** @cfg {Boolean} grow @hide */
9338 /** @cfg {Number} growMin @hide */
9339 /** @cfg {Number} growMax @hide */
9345 autoSize: Roo.emptyFn,
9352 actionMode : 'wrap',
9357 getAutoCreate : function(){
9359 var align = this.labelAlign || this.parentLabelAlign();
9364 cls: 'form-group' //input-group
9371 type : this.inputType,
9372 cls : 'form-control',
9373 autocomplete: 'new-password',
9374 placeholder : this.placeholder || ''
9378 input.name = this.name;
9381 input.cls += ' input-' + this.size;
9384 if (this.disabled) {
9385 input.disabled=true;
9388 var inputblock = input;
9390 if(this.hasFeedback && !this.allowBlank){
9394 cls: 'glyphicon form-control-feedback'
9397 if(this.removable && !this.editable && !this.tickable){
9399 cls : 'has-feedback',
9405 cls : 'roo-combo-removable-btn close'
9412 cls : 'has-feedback',
9421 if(this.removable && !this.editable && !this.tickable){
9423 cls : 'roo-removable',
9429 cls : 'roo-combo-removable-btn close'
9436 if (this.before || this.after) {
9439 cls : 'input-group',
9443 inputblock.cn.push({
9445 cls : 'input-group-addon',
9450 inputblock.cn.push(input);
9452 if(this.hasFeedback && !this.allowBlank){
9453 inputblock.cls += ' has-feedback';
9454 inputblock.cn.push(feedback);
9458 inputblock.cn.push({
9460 cls : 'input-group-addon',
9473 cls: 'form-hidden-field'
9487 cls: 'form-hidden-field'
9491 cls: 'roo-select2-choices',
9495 cls: 'roo-select2-search-field',
9508 cls: 'roo-select2-container input-group',
9513 // cls: 'typeahead typeahead-long dropdown-menu',
9514 // style: 'display:none'
9519 if(!this.multiple && this.showToggleBtn){
9525 if (this.caret != false) {
9528 cls: 'fa fa-' + this.caret
9535 cls : 'input-group-addon btn dropdown-toggle',
9540 cls: 'combobox-clear',
9554 combobox.cls += ' roo-select2-container-multi';
9557 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9559 // Roo.log("left and has label");
9563 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9564 tooltip : 'This field is required'
9569 cls : 'control-label col-sm-' + this.labelWidth,
9570 html : this.fieldLabel
9574 cls : "col-sm-" + (12 - this.labelWidth),
9582 if(this.indicatorpos == 'right'){
9587 cls : 'control-label col-sm-' + this.labelWidth,
9588 html : this.fieldLabel
9593 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9594 tooltip : 'This field is required'
9597 cls : "col-sm-" + (12 - this.labelWidth),
9606 } else if ( this.fieldLabel.length) {
9607 // Roo.log(" label");
9611 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9612 tooltip : 'This field is required'
9616 //cls : 'input-group-addon',
9617 html : this.fieldLabel
9625 if(this.indicatorpos == 'right'){
9630 //cls : 'input-group-addon',
9631 html : this.fieldLabel
9636 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9637 tooltip : 'This field is required'
9648 // Roo.log(" no label && no align");
9655 ['xs','sm','md','lg'].map(function(size){
9656 if (settings[size]) {
9657 cfg.cls += ' col-' + size + '-' + settings[size];
9668 onResize : function(w, h){
9669 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9670 // if(typeof w == 'number'){
9671 // var x = w - this.trigger.getWidth();
9672 // this.inputEl().setWidth(this.adjustWidth('input', x));
9673 // this.trigger.setStyle('left', x+'px');
9678 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9681 getResizeEl : function(){
9682 return this.inputEl();
9686 getPositionEl : function(){
9687 return this.inputEl();
9691 alignErrorIcon : function(){
9692 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9696 initEvents : function(){
9700 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9701 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9702 if(!this.multiple && this.showToggleBtn){
9703 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9704 if(this.hideTrigger){
9705 this.trigger.setDisplayed(false);
9707 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9711 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9714 if(this.removable && !this.editable && !this.tickable){
9715 var close = this.closeTriggerEl();
9718 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9719 close.on('click', this.removeBtnClick, this, close);
9723 //this.trigger.addClassOnOver('x-form-trigger-over');
9724 //this.trigger.addClassOnClick('x-form-trigger-click');
9727 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9731 closeTriggerEl : function()
9733 var close = this.el.select('.roo-combo-removable-btn', true).first();
9734 return close ? close : false;
9737 removeBtnClick : function(e, h, el)
9741 if(this.fireEvent("remove", this) !== false){
9743 this.fireEvent("afterremove", this)
9747 createList : function()
9749 this.list = Roo.get(document.body).createChild({
9751 cls: 'typeahead typeahead-long dropdown-menu',
9752 style: 'display:none'
9755 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9760 initTrigger : function(){
9765 onDestroy : function(){
9767 this.trigger.removeAllListeners();
9768 // this.trigger.remove();
9771 // this.wrap.remove();
9773 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9777 onFocus : function(){
9778 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9781 this.wrap.addClass('x-trigger-wrap-focus');
9782 this.mimicing = true;
9783 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9784 if(this.monitorTab){
9785 this.el.on("keydown", this.checkTab, this);
9792 checkTab : function(e){
9793 if(e.getKey() == e.TAB){
9799 onBlur : function(){
9804 mimicBlur : function(e, t){
9806 if(!this.wrap.contains(t) && this.validateBlur()){
9813 triggerBlur : function(){
9814 this.mimicing = false;
9815 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9816 if(this.monitorTab){
9817 this.el.un("keydown", this.checkTab, this);
9819 //this.wrap.removeClass('x-trigger-wrap-focus');
9820 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9824 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9825 validateBlur : function(e, t){
9830 onDisable : function(){
9831 this.inputEl().dom.disabled = true;
9832 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9834 // this.wrap.addClass('x-item-disabled');
9839 onEnable : function(){
9840 this.inputEl().dom.disabled = false;
9841 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9843 // this.el.removeClass('x-item-disabled');
9848 onShow : function(){
9849 var ae = this.getActionEl();
9852 ae.dom.style.display = '';
9853 ae.dom.style.visibility = 'visible';
9859 onHide : function(){
9860 var ae = this.getActionEl();
9861 ae.dom.style.display = 'none';
9865 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9866 * by an implementing function.
9868 * @param {EventObject} e
9870 onTriggerClick : Roo.emptyFn
9874 * Ext JS Library 1.1.1
9875 * Copyright(c) 2006-2007, Ext JS, LLC.
9877 * Originally Released Under LGPL - original licence link has changed is not relivant.
9880 * <script type="text/javascript">
9885 * @class Roo.data.SortTypes
9887 * Defines the default sorting (casting?) comparison functions used when sorting data.
9889 Roo.data.SortTypes = {
9891 * Default sort that does nothing
9892 * @param {Mixed} s The value being converted
9893 * @return {Mixed} The comparison value
9900 * The regular expression used to strip tags
9904 stripTagsRE : /<\/?[^>]+>/gi,
9907 * Strips all HTML tags to sort on text only
9908 * @param {Mixed} s The value being converted
9909 * @return {String} The comparison value
9911 asText : function(s){
9912 return String(s).replace(this.stripTagsRE, "");
9916 * Strips all HTML tags to sort on text only - Case insensitive
9917 * @param {Mixed} s The value being converted
9918 * @return {String} The comparison value
9920 asUCText : function(s){
9921 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9925 * Case insensitive string
9926 * @param {Mixed} s The value being converted
9927 * @return {String} The comparison value
9929 asUCString : function(s) {
9930 return String(s).toUpperCase();
9935 * @param {Mixed} s The value being converted
9936 * @return {Number} The comparison value
9938 asDate : function(s) {
9942 if(s instanceof Date){
9945 return Date.parse(String(s));
9950 * @param {Mixed} s The value being converted
9951 * @return {Float} The comparison value
9953 asFloat : function(s) {
9954 var val = parseFloat(String(s).replace(/,/g, ""));
9963 * @param {Mixed} s The value being converted
9964 * @return {Number} The comparison value
9966 asInt : function(s) {
9967 var val = parseInt(String(s).replace(/,/g, ""));
9975 * Ext JS Library 1.1.1
9976 * Copyright(c) 2006-2007, Ext JS, LLC.
9978 * Originally Released Under LGPL - original licence link has changed is not relivant.
9981 * <script type="text/javascript">
9985 * @class Roo.data.Record
9986 * Instances of this class encapsulate both record <em>definition</em> information, and record
9987 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9988 * to access Records cached in an {@link Roo.data.Store} object.<br>
9990 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9991 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9994 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9996 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9997 * {@link #create}. The parameters are the same.
9998 * @param {Array} data An associative Array of data values keyed by the field name.
9999 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10000 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10001 * not specified an integer id is generated.
10003 Roo.data.Record = function(data, id){
10004 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10009 * Generate a constructor for a specific record layout.
10010 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10011 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10012 * Each field definition object may contain the following properties: <ul>
10013 * <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,
10014 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10015 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10016 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10017 * is being used, then this is a string containing the javascript expression to reference the data relative to
10018 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10019 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10020 * this may be omitted.</p></li>
10021 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10022 * <ul><li>auto (Default, implies no conversion)</li>
10027 * <li>date</li></ul></p></li>
10028 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10029 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10030 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10031 * by the Reader into an object that will be stored in the Record. It is passed the
10032 * following parameters:<ul>
10033 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10035 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10037 * <br>usage:<br><pre><code>
10038 var TopicRecord = Roo.data.Record.create(
10039 {name: 'title', mapping: 'topic_title'},
10040 {name: 'author', mapping: 'username'},
10041 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10042 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10043 {name: 'lastPoster', mapping: 'user2'},
10044 {name: 'excerpt', mapping: 'post_text'}
10047 var myNewRecord = new TopicRecord({
10048 title: 'Do my job please',
10051 lastPost: new Date(),
10052 lastPoster: 'Animal',
10053 excerpt: 'No way dude!'
10055 myStore.add(myNewRecord);
10060 Roo.data.Record.create = function(o){
10061 var f = function(){
10062 f.superclass.constructor.apply(this, arguments);
10064 Roo.extend(f, Roo.data.Record);
10065 var p = f.prototype;
10066 p.fields = new Roo.util.MixedCollection(false, function(field){
10069 for(var i = 0, len = o.length; i < len; i++){
10070 p.fields.add(new Roo.data.Field(o[i]));
10072 f.getField = function(name){
10073 return p.fields.get(name);
10078 Roo.data.Record.AUTO_ID = 1000;
10079 Roo.data.Record.EDIT = 'edit';
10080 Roo.data.Record.REJECT = 'reject';
10081 Roo.data.Record.COMMIT = 'commit';
10083 Roo.data.Record.prototype = {
10085 * Readonly flag - true if this record has been modified.
10094 join : function(store){
10095 this.store = store;
10099 * Set the named field to the specified value.
10100 * @param {String} name The name of the field to set.
10101 * @param {Object} value The value to set the field to.
10103 set : function(name, value){
10104 if(this.data[name] == value){
10108 if(!this.modified){
10109 this.modified = {};
10111 if(typeof this.modified[name] == 'undefined'){
10112 this.modified[name] = this.data[name];
10114 this.data[name] = value;
10115 if(!this.editing && this.store){
10116 this.store.afterEdit(this);
10121 * Get the value of the named field.
10122 * @param {String} name The name of the field to get the value of.
10123 * @return {Object} The value of the field.
10125 get : function(name){
10126 return this.data[name];
10130 beginEdit : function(){
10131 this.editing = true;
10132 this.modified = {};
10136 cancelEdit : function(){
10137 this.editing = false;
10138 delete this.modified;
10142 endEdit : function(){
10143 this.editing = false;
10144 if(this.dirty && this.store){
10145 this.store.afterEdit(this);
10150 * Usually called by the {@link Roo.data.Store} which owns the Record.
10151 * Rejects all changes made to the Record since either creation, or the last commit operation.
10152 * Modified fields are reverted to their original values.
10154 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10155 * of reject operations.
10157 reject : function(){
10158 var m = this.modified;
10160 if(typeof m[n] != "function"){
10161 this.data[n] = m[n];
10164 this.dirty = false;
10165 delete this.modified;
10166 this.editing = false;
10168 this.store.afterReject(this);
10173 * Usually called by the {@link Roo.data.Store} which owns the Record.
10174 * Commits all changes made to the Record since either creation, or the last commit operation.
10176 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10177 * of commit operations.
10179 commit : function(){
10180 this.dirty = false;
10181 delete this.modified;
10182 this.editing = false;
10184 this.store.afterCommit(this);
10189 hasError : function(){
10190 return this.error != null;
10194 clearError : function(){
10199 * Creates a copy of this record.
10200 * @param {String} id (optional) A new record id if you don't want to use this record's id
10203 copy : function(newId) {
10204 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10208 * Ext JS Library 1.1.1
10209 * Copyright(c) 2006-2007, Ext JS, LLC.
10211 * Originally Released Under LGPL - original licence link has changed is not relivant.
10214 * <script type="text/javascript">
10220 * @class Roo.data.Store
10221 * @extends Roo.util.Observable
10222 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10223 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10225 * 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
10226 * has no knowledge of the format of the data returned by the Proxy.<br>
10228 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10229 * instances from the data object. These records are cached and made available through accessor functions.
10231 * Creates a new Store.
10232 * @param {Object} config A config object containing the objects needed for the Store to access data,
10233 * and read the data into Records.
10235 Roo.data.Store = function(config){
10236 this.data = new Roo.util.MixedCollection(false);
10237 this.data.getKey = function(o){
10240 this.baseParams = {};
10242 this.paramNames = {
10247 "multisort" : "_multisort"
10250 if(config && config.data){
10251 this.inlineData = config.data;
10252 delete config.data;
10255 Roo.apply(this, config);
10257 if(this.reader){ // reader passed
10258 this.reader = Roo.factory(this.reader, Roo.data);
10259 this.reader.xmodule = this.xmodule || false;
10260 if(!this.recordType){
10261 this.recordType = this.reader.recordType;
10263 if(this.reader.onMetaChange){
10264 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10268 if(this.recordType){
10269 this.fields = this.recordType.prototype.fields;
10271 this.modified = [];
10275 * @event datachanged
10276 * Fires when the data cache has changed, and a widget which is using this Store
10277 * as a Record cache should refresh its view.
10278 * @param {Store} this
10280 datachanged : true,
10282 * @event metachange
10283 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10284 * @param {Store} this
10285 * @param {Object} meta The JSON metadata
10290 * Fires when Records have been added to the Store
10291 * @param {Store} this
10292 * @param {Roo.data.Record[]} records The array of Records added
10293 * @param {Number} index The index at which the record(s) were added
10298 * Fires when a Record has been removed from the Store
10299 * @param {Store} this
10300 * @param {Roo.data.Record} record The Record that was removed
10301 * @param {Number} index The index at which the record was removed
10306 * Fires when a Record has been updated
10307 * @param {Store} this
10308 * @param {Roo.data.Record} record The Record that was updated
10309 * @param {String} operation The update operation being performed. Value may be one of:
10311 Roo.data.Record.EDIT
10312 Roo.data.Record.REJECT
10313 Roo.data.Record.COMMIT
10319 * Fires when the data cache has been cleared.
10320 * @param {Store} this
10324 * @event beforeload
10325 * Fires before a request is made for a new data object. If the beforeload handler returns false
10326 * the load action will be canceled.
10327 * @param {Store} this
10328 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10332 * @event beforeloadadd
10333 * Fires after a new set of Records has been loaded.
10334 * @param {Store} this
10335 * @param {Roo.data.Record[]} records The Records that were loaded
10336 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10338 beforeloadadd : true,
10341 * Fires after a new set of Records has been loaded, before they are added to the store.
10342 * @param {Store} this
10343 * @param {Roo.data.Record[]} records The Records that were loaded
10344 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10345 * @params {Object} return from reader
10349 * @event loadexception
10350 * Fires if an exception occurs in the Proxy during loading.
10351 * Called with the signature of the Proxy's "loadexception" event.
10352 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10355 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10356 * @param {Object} load options
10357 * @param {Object} jsonData from your request (normally this contains the Exception)
10359 loadexception : true
10363 this.proxy = Roo.factory(this.proxy, Roo.data);
10364 this.proxy.xmodule = this.xmodule || false;
10365 this.relayEvents(this.proxy, ["loadexception"]);
10367 this.sortToggle = {};
10368 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10370 Roo.data.Store.superclass.constructor.call(this);
10372 if(this.inlineData){
10373 this.loadData(this.inlineData);
10374 delete this.inlineData;
10378 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10380 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10381 * without a remote query - used by combo/forms at present.
10385 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10388 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10391 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10392 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10395 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10396 * on any HTTP request
10399 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10402 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10406 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10407 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10409 remoteSort : false,
10412 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10413 * loaded or when a record is removed. (defaults to false).
10415 pruneModifiedRecords : false,
10418 lastOptions : null,
10421 * Add Records to the Store and fires the add event.
10422 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10424 add : function(records){
10425 records = [].concat(records);
10426 for(var i = 0, len = records.length; i < len; i++){
10427 records[i].join(this);
10429 var index = this.data.length;
10430 this.data.addAll(records);
10431 this.fireEvent("add", this, records, index);
10435 * Remove a Record from the Store and fires the remove event.
10436 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10438 remove : function(record){
10439 var index = this.data.indexOf(record);
10440 this.data.removeAt(index);
10441 if(this.pruneModifiedRecords){
10442 this.modified.remove(record);
10444 this.fireEvent("remove", this, record, index);
10448 * Remove all Records from the Store and fires the clear event.
10450 removeAll : function(){
10452 if(this.pruneModifiedRecords){
10453 this.modified = [];
10455 this.fireEvent("clear", this);
10459 * Inserts Records to the Store at the given index and fires the add event.
10460 * @param {Number} index The start index at which to insert the passed Records.
10461 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10463 insert : function(index, records){
10464 records = [].concat(records);
10465 for(var i = 0, len = records.length; i < len; i++){
10466 this.data.insert(index, records[i]);
10467 records[i].join(this);
10469 this.fireEvent("add", this, records, index);
10473 * Get the index within the cache of the passed Record.
10474 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10475 * @return {Number} The index of the passed Record. Returns -1 if not found.
10477 indexOf : function(record){
10478 return this.data.indexOf(record);
10482 * Get the index within the cache of the Record with the passed id.
10483 * @param {String} id The id of the Record to find.
10484 * @return {Number} The index of the Record. Returns -1 if not found.
10486 indexOfId : function(id){
10487 return this.data.indexOfKey(id);
10491 * Get the Record with the specified id.
10492 * @param {String} id The id of the Record to find.
10493 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10495 getById : function(id){
10496 return this.data.key(id);
10500 * Get the Record at the specified index.
10501 * @param {Number} index The index of the Record to find.
10502 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10504 getAt : function(index){
10505 return this.data.itemAt(index);
10509 * Returns a range of Records between specified indices.
10510 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10511 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10512 * @return {Roo.data.Record[]} An array of Records
10514 getRange : function(start, end){
10515 return this.data.getRange(start, end);
10519 storeOptions : function(o){
10520 o = Roo.apply({}, o);
10523 this.lastOptions = o;
10527 * Loads the Record cache from the configured Proxy using the configured Reader.
10529 * If using remote paging, then the first load call must specify the <em>start</em>
10530 * and <em>limit</em> properties in the options.params property to establish the initial
10531 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10533 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10534 * and this call will return before the new data has been loaded. Perform any post-processing
10535 * in a callback function, or in a "load" event handler.</strong>
10537 * @param {Object} options An object containing properties which control loading options:<ul>
10538 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10539 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10540 * passed the following arguments:<ul>
10541 * <li>r : Roo.data.Record[]</li>
10542 * <li>options: Options object from the load call</li>
10543 * <li>success: Boolean success indicator</li></ul></li>
10544 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10545 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10548 load : function(options){
10549 options = options || {};
10550 if(this.fireEvent("beforeload", this, options) !== false){
10551 this.storeOptions(options);
10552 var p = Roo.apply(options.params || {}, this.baseParams);
10553 // if meta was not loaded from remote source.. try requesting it.
10554 if (!this.reader.metaFromRemote) {
10555 p._requestMeta = 1;
10557 if(this.sortInfo && this.remoteSort){
10558 var pn = this.paramNames;
10559 p[pn["sort"]] = this.sortInfo.field;
10560 p[pn["dir"]] = this.sortInfo.direction;
10562 if (this.multiSort) {
10563 var pn = this.paramNames;
10564 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10567 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10572 * Reloads the Record cache from the configured Proxy using the configured Reader and
10573 * the options from the last load operation performed.
10574 * @param {Object} options (optional) An object containing properties which may override the options
10575 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10576 * the most recently used options are reused).
10578 reload : function(options){
10579 this.load(Roo.applyIf(options||{}, this.lastOptions));
10583 // Called as a callback by the Reader during a load operation.
10584 loadRecords : function(o, options, success){
10585 if(!o || success === false){
10586 if(success !== false){
10587 this.fireEvent("load", this, [], options, o);
10589 if(options.callback){
10590 options.callback.call(options.scope || this, [], options, false);
10594 // if data returned failure - throw an exception.
10595 if (o.success === false) {
10596 // show a message if no listener is registered.
10597 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10598 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10600 // loadmask wil be hooked into this..
10601 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10604 var r = o.records, t = o.totalRecords || r.length;
10606 this.fireEvent("beforeloadadd", this, r, options, o);
10608 if(!options || options.add !== true){
10609 if(this.pruneModifiedRecords){
10610 this.modified = [];
10612 for(var i = 0, len = r.length; i < len; i++){
10616 this.data = this.snapshot;
10617 delete this.snapshot;
10620 this.data.addAll(r);
10621 this.totalLength = t;
10623 this.fireEvent("datachanged", this);
10625 this.totalLength = Math.max(t, this.data.length+r.length);
10628 this.fireEvent("load", this, r, options, o);
10629 if(options.callback){
10630 options.callback.call(options.scope || this, r, options, true);
10636 * Loads data from a passed data block. A Reader which understands the format of the data
10637 * must have been configured in the constructor.
10638 * @param {Object} data The data block from which to read the Records. The format of the data expected
10639 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10640 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10642 loadData : function(o, append){
10643 var r = this.reader.readRecords(o);
10644 this.loadRecords(r, {add: append}, true);
10648 * Gets the number of cached records.
10650 * <em>If using paging, this may not be the total size of the dataset. If the data object
10651 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10652 * the data set size</em>
10654 getCount : function(){
10655 return this.data.length || 0;
10659 * Gets the total number of records in the dataset as returned by the server.
10661 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10662 * the dataset size</em>
10664 getTotalCount : function(){
10665 return this.totalLength || 0;
10669 * Returns the sort state of the Store as an object with two properties:
10671 field {String} The name of the field by which the Records are sorted
10672 direction {String} The sort order, "ASC" or "DESC"
10675 getSortState : function(){
10676 return this.sortInfo;
10680 applySort : function(){
10681 if(this.sortInfo && !this.remoteSort){
10682 var s = this.sortInfo, f = s.field;
10683 var st = this.fields.get(f).sortType;
10684 var fn = function(r1, r2){
10685 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10686 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10688 this.data.sort(s.direction, fn);
10689 if(this.snapshot && this.snapshot != this.data){
10690 this.snapshot.sort(s.direction, fn);
10696 * Sets the default sort column and order to be used by the next load operation.
10697 * @param {String} fieldName The name of the field to sort by.
10698 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10700 setDefaultSort : function(field, dir){
10701 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10705 * Sort the Records.
10706 * If remote sorting is used, the sort is performed on the server, and the cache is
10707 * reloaded. If local sorting is used, the cache is sorted internally.
10708 * @param {String} fieldName The name of the field to sort by.
10709 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10711 sort : function(fieldName, dir){
10712 var f = this.fields.get(fieldName);
10714 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10716 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10717 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10722 this.sortToggle[f.name] = dir;
10723 this.sortInfo = {field: f.name, direction: dir};
10724 if(!this.remoteSort){
10726 this.fireEvent("datachanged", this);
10728 this.load(this.lastOptions);
10733 * Calls the specified function for each of the Records in the cache.
10734 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10735 * Returning <em>false</em> aborts and exits the iteration.
10736 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10738 each : function(fn, scope){
10739 this.data.each(fn, scope);
10743 * Gets all records modified since the last commit. Modified records are persisted across load operations
10744 * (e.g., during paging).
10745 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10747 getModifiedRecords : function(){
10748 return this.modified;
10752 createFilterFn : function(property, value, anyMatch){
10753 if(!value.exec){ // not a regex
10754 value = String(value);
10755 if(value.length == 0){
10758 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10760 return function(r){
10761 return value.test(r.data[property]);
10766 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10767 * @param {String} property A field on your records
10768 * @param {Number} start The record index to start at (defaults to 0)
10769 * @param {Number} end The last record index to include (defaults to length - 1)
10770 * @return {Number} The sum
10772 sum : function(property, start, end){
10773 var rs = this.data.items, v = 0;
10774 start = start || 0;
10775 end = (end || end === 0) ? end : rs.length-1;
10777 for(var i = start; i <= end; i++){
10778 v += (rs[i].data[property] || 0);
10784 * Filter the records by a specified property.
10785 * @param {String} field A field on your records
10786 * @param {String/RegExp} value Either a string that the field
10787 * should start with or a RegExp to test against the field
10788 * @param {Boolean} anyMatch True to match any part not just the beginning
10790 filter : function(property, value, anyMatch){
10791 var fn = this.createFilterFn(property, value, anyMatch);
10792 return fn ? this.filterBy(fn) : this.clearFilter();
10796 * Filter by a function. The specified function will be called with each
10797 * record in this data source. If the function returns true the record is included,
10798 * otherwise it is filtered.
10799 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10800 * @param {Object} scope (optional) The scope of the function (defaults to this)
10802 filterBy : function(fn, scope){
10803 this.snapshot = this.snapshot || this.data;
10804 this.data = this.queryBy(fn, scope||this);
10805 this.fireEvent("datachanged", this);
10809 * Query the records by a specified property.
10810 * @param {String} field A field on your records
10811 * @param {String/RegExp} value Either a string that the field
10812 * should start with or a RegExp to test against the field
10813 * @param {Boolean} anyMatch True to match any part not just the beginning
10814 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10816 query : function(property, value, anyMatch){
10817 var fn = this.createFilterFn(property, value, anyMatch);
10818 return fn ? this.queryBy(fn) : this.data.clone();
10822 * Query by a function. The specified function will be called with each
10823 * record in this data source. If the function returns true the record is included
10825 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10826 * @param {Object} scope (optional) The scope of the function (defaults to this)
10827 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10829 queryBy : function(fn, scope){
10830 var data = this.snapshot || this.data;
10831 return data.filterBy(fn, scope||this);
10835 * Collects unique values for a particular dataIndex from this store.
10836 * @param {String} dataIndex The property to collect
10837 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10838 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10839 * @return {Array} An array of the unique values
10841 collect : function(dataIndex, allowNull, bypassFilter){
10842 var d = (bypassFilter === true && this.snapshot) ?
10843 this.snapshot.items : this.data.items;
10844 var v, sv, r = [], l = {};
10845 for(var i = 0, len = d.length; i < len; i++){
10846 v = d[i].data[dataIndex];
10848 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10857 * Revert to a view of the Record cache with no filtering applied.
10858 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10860 clearFilter : function(suppressEvent){
10861 if(this.snapshot && this.snapshot != this.data){
10862 this.data = this.snapshot;
10863 delete this.snapshot;
10864 if(suppressEvent !== true){
10865 this.fireEvent("datachanged", this);
10871 afterEdit : function(record){
10872 if(this.modified.indexOf(record) == -1){
10873 this.modified.push(record);
10875 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10879 afterReject : function(record){
10880 this.modified.remove(record);
10881 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10885 afterCommit : function(record){
10886 this.modified.remove(record);
10887 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10891 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10892 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10894 commitChanges : function(){
10895 var m = this.modified.slice(0);
10896 this.modified = [];
10897 for(var i = 0, len = m.length; i < len; i++){
10903 * Cancel outstanding changes on all changed records.
10905 rejectChanges : function(){
10906 var m = this.modified.slice(0);
10907 this.modified = [];
10908 for(var i = 0, len = m.length; i < len; i++){
10913 onMetaChange : function(meta, rtype, o){
10914 this.recordType = rtype;
10915 this.fields = rtype.prototype.fields;
10916 delete this.snapshot;
10917 this.sortInfo = meta.sortInfo || this.sortInfo;
10918 this.modified = [];
10919 this.fireEvent('metachange', this, this.reader.meta);
10922 moveIndex : function(data, type)
10924 var index = this.indexOf(data);
10926 var newIndex = index + type;
10930 this.insert(newIndex, data);
10935 * Ext JS Library 1.1.1
10936 * Copyright(c) 2006-2007, Ext JS, LLC.
10938 * Originally Released Under LGPL - original licence link has changed is not relivant.
10941 * <script type="text/javascript">
10945 * @class Roo.data.SimpleStore
10946 * @extends Roo.data.Store
10947 * Small helper class to make creating Stores from Array data easier.
10948 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10949 * @cfg {Array} fields An array of field definition objects, or field name strings.
10950 * @cfg {Array} data The multi-dimensional array of data
10952 * @param {Object} config
10954 Roo.data.SimpleStore = function(config){
10955 Roo.data.SimpleStore.superclass.constructor.call(this, {
10957 reader: new Roo.data.ArrayReader({
10960 Roo.data.Record.create(config.fields)
10962 proxy : new Roo.data.MemoryProxy(config.data)
10966 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10968 * Ext JS Library 1.1.1
10969 * Copyright(c) 2006-2007, Ext JS, LLC.
10971 * Originally Released Under LGPL - original licence link has changed is not relivant.
10974 * <script type="text/javascript">
10979 * @extends Roo.data.Store
10980 * @class Roo.data.JsonStore
10981 * Small helper class to make creating Stores for JSON data easier. <br/>
10983 var store = new Roo.data.JsonStore({
10984 url: 'get-images.php',
10986 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10989 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10990 * JsonReader and HttpProxy (unless inline data is provided).</b>
10991 * @cfg {Array} fields An array of field definition objects, or field name strings.
10993 * @param {Object} config
10995 Roo.data.JsonStore = function(c){
10996 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10997 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10998 reader: new Roo.data.JsonReader(c, c.fields)
11001 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11003 * Ext JS Library 1.1.1
11004 * Copyright(c) 2006-2007, Ext JS, LLC.
11006 * Originally Released Under LGPL - original licence link has changed is not relivant.
11009 * <script type="text/javascript">
11013 Roo.data.Field = function(config){
11014 if(typeof config == "string"){
11015 config = {name: config};
11017 Roo.apply(this, config);
11020 this.type = "auto";
11023 var st = Roo.data.SortTypes;
11024 // named sortTypes are supported, here we look them up
11025 if(typeof this.sortType == "string"){
11026 this.sortType = st[this.sortType];
11029 // set default sortType for strings and dates
11030 if(!this.sortType){
11033 this.sortType = st.asUCString;
11036 this.sortType = st.asDate;
11039 this.sortType = st.none;
11044 var stripRe = /[\$,%]/g;
11046 // prebuilt conversion function for this field, instead of
11047 // switching every time we're reading a value
11049 var cv, dateFormat = this.dateFormat;
11054 cv = function(v){ return v; };
11057 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11061 return v !== undefined && v !== null && v !== '' ?
11062 parseInt(String(v).replace(stripRe, ""), 10) : '';
11067 return v !== undefined && v !== null && v !== '' ?
11068 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11073 cv = function(v){ return v === true || v === "true" || v == 1; };
11080 if(v instanceof Date){
11084 if(dateFormat == "timestamp"){
11085 return new Date(v*1000);
11087 return Date.parseDate(v, dateFormat);
11089 var parsed = Date.parse(v);
11090 return parsed ? new Date(parsed) : null;
11099 Roo.data.Field.prototype = {
11107 * Ext JS Library 1.1.1
11108 * Copyright(c) 2006-2007, Ext JS, LLC.
11110 * Originally Released Under LGPL - original licence link has changed is not relivant.
11113 * <script type="text/javascript">
11116 // Base class for reading structured data from a data source. This class is intended to be
11117 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11120 * @class Roo.data.DataReader
11121 * Base class for reading structured data from a data source. This class is intended to be
11122 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11125 Roo.data.DataReader = function(meta, recordType){
11129 this.recordType = recordType instanceof Array ?
11130 Roo.data.Record.create(recordType) : recordType;
11133 Roo.data.DataReader.prototype = {
11135 * Create an empty record
11136 * @param {Object} data (optional) - overlay some values
11137 * @return {Roo.data.Record} record created.
11139 newRow : function(d) {
11141 this.recordType.prototype.fields.each(function(c) {
11143 case 'int' : da[c.name] = 0; break;
11144 case 'date' : da[c.name] = new Date(); break;
11145 case 'float' : da[c.name] = 0.0; break;
11146 case 'boolean' : da[c.name] = false; break;
11147 default : da[c.name] = ""; break;
11151 return new this.recordType(Roo.apply(da, d));
11156 * Ext JS Library 1.1.1
11157 * Copyright(c) 2006-2007, Ext JS, LLC.
11159 * Originally Released Under LGPL - original licence link has changed is not relivant.
11162 * <script type="text/javascript">
11166 * @class Roo.data.DataProxy
11167 * @extends Roo.data.Observable
11168 * This class is an abstract base class for implementations which provide retrieval of
11169 * unformatted data objects.<br>
11171 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11172 * (of the appropriate type which knows how to parse the data object) to provide a block of
11173 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11175 * Custom implementations must implement the load method as described in
11176 * {@link Roo.data.HttpProxy#load}.
11178 Roo.data.DataProxy = function(){
11181 * @event beforeload
11182 * Fires before a network request is made to retrieve a data object.
11183 * @param {Object} This DataProxy object.
11184 * @param {Object} params The params parameter to the load function.
11189 * Fires before the load method's callback is called.
11190 * @param {Object} This DataProxy object.
11191 * @param {Object} o The data object.
11192 * @param {Object} arg The callback argument object passed to the load function.
11196 * @event loadexception
11197 * Fires if an Exception occurs during data retrieval.
11198 * @param {Object} This DataProxy object.
11199 * @param {Object} o The data object.
11200 * @param {Object} arg The callback argument object passed to the load function.
11201 * @param {Object} e The Exception.
11203 loadexception : true
11205 Roo.data.DataProxy.superclass.constructor.call(this);
11208 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11211 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11215 * Ext JS Library 1.1.1
11216 * Copyright(c) 2006-2007, Ext JS, LLC.
11218 * Originally Released Under LGPL - original licence link has changed is not relivant.
11221 * <script type="text/javascript">
11224 * @class Roo.data.MemoryProxy
11225 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11226 * to the Reader when its load method is called.
11228 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11230 Roo.data.MemoryProxy = function(data){
11234 Roo.data.MemoryProxy.superclass.constructor.call(this);
11238 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11241 * Load data from the requested source (in this case an in-memory
11242 * data object passed to the constructor), read the data object into
11243 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11244 * process that block using the passed callback.
11245 * @param {Object} params This parameter is not used by the MemoryProxy class.
11246 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11247 * object into a block of Roo.data.Records.
11248 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11249 * The function must be passed <ul>
11250 * <li>The Record block object</li>
11251 * <li>The "arg" argument from the load function</li>
11252 * <li>A boolean success indicator</li>
11254 * @param {Object} scope The scope in which to call the callback
11255 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11257 load : function(params, reader, callback, scope, arg){
11258 params = params || {};
11261 result = reader.readRecords(this.data);
11263 this.fireEvent("loadexception", this, arg, null, e);
11264 callback.call(scope, null, arg, false);
11267 callback.call(scope, result, arg, true);
11271 update : function(params, records){
11276 * Ext JS Library 1.1.1
11277 * Copyright(c) 2006-2007, Ext JS, LLC.
11279 * Originally Released Under LGPL - original licence link has changed is not relivant.
11282 * <script type="text/javascript">
11285 * @class Roo.data.HttpProxy
11286 * @extends Roo.data.DataProxy
11287 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11288 * configured to reference a certain URL.<br><br>
11290 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11291 * from which the running page was served.<br><br>
11293 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11295 * Be aware that to enable the browser to parse an XML document, the server must set
11296 * the Content-Type header in the HTTP response to "text/xml".
11298 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11299 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11300 * will be used to make the request.
11302 Roo.data.HttpProxy = function(conn){
11303 Roo.data.HttpProxy.superclass.constructor.call(this);
11304 // is conn a conn config or a real conn?
11306 this.useAjax = !conn || !conn.events;
11310 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11311 // thse are take from connection...
11314 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11317 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11318 * extra parameters to each request made by this object. (defaults to undefined)
11321 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11322 * to each request made by this object. (defaults to undefined)
11325 * @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)
11328 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11331 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11337 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11341 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11342 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11343 * a finer-grained basis than the DataProxy events.
11345 getConnection : function(){
11346 return this.useAjax ? Roo.Ajax : this.conn;
11350 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11351 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11352 * process that block using the passed callback.
11353 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11354 * for the request to the remote server.
11355 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11356 * object into a block of Roo.data.Records.
11357 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11358 * The function must be passed <ul>
11359 * <li>The Record block object</li>
11360 * <li>The "arg" argument from the load function</li>
11361 * <li>A boolean success indicator</li>
11363 * @param {Object} scope The scope in which to call the callback
11364 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11366 load : function(params, reader, callback, scope, arg){
11367 if(this.fireEvent("beforeload", this, params) !== false){
11369 params : params || {},
11371 callback : callback,
11376 callback : this.loadResponse,
11380 Roo.applyIf(o, this.conn);
11381 if(this.activeRequest){
11382 Roo.Ajax.abort(this.activeRequest);
11384 this.activeRequest = Roo.Ajax.request(o);
11386 this.conn.request(o);
11389 callback.call(scope||this, null, arg, false);
11394 loadResponse : function(o, success, response){
11395 delete this.activeRequest;
11397 this.fireEvent("loadexception", this, o, response);
11398 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11403 result = o.reader.read(response);
11405 this.fireEvent("loadexception", this, o, response, e);
11406 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11410 this.fireEvent("load", this, o, o.request.arg);
11411 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11415 update : function(dataSet){
11420 updateResponse : function(dataSet){
11425 * Ext JS Library 1.1.1
11426 * Copyright(c) 2006-2007, Ext JS, LLC.
11428 * Originally Released Under LGPL - original licence link has changed is not relivant.
11431 * <script type="text/javascript">
11435 * @class Roo.data.ScriptTagProxy
11436 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11437 * other than the originating domain of the running page.<br><br>
11439 * <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
11440 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11442 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11443 * source code that is used as the source inside a <script> tag.<br><br>
11445 * In order for the browser to process the returned data, the server must wrap the data object
11446 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11447 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11448 * depending on whether the callback name was passed:
11451 boolean scriptTag = false;
11452 String cb = request.getParameter("callback");
11455 response.setContentType("text/javascript");
11457 response.setContentType("application/x-json");
11459 Writer out = response.getWriter();
11461 out.write(cb + "(");
11463 out.print(dataBlock.toJsonString());
11470 * @param {Object} config A configuration object.
11472 Roo.data.ScriptTagProxy = function(config){
11473 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11474 Roo.apply(this, config);
11475 this.head = document.getElementsByTagName("head")[0];
11478 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11480 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11482 * @cfg {String} url The URL from which to request the data object.
11485 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11489 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11490 * the server the name of the callback function set up by the load call to process the returned data object.
11491 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11492 * javascript output which calls this named function passing the data object as its only parameter.
11494 callbackParam : "callback",
11496 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11497 * name to the request.
11502 * Load data from the configured URL, read the data object into
11503 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11504 * process that block using the passed callback.
11505 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11506 * for the request to the remote server.
11507 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11508 * object into a block of Roo.data.Records.
11509 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11510 * The function must be passed <ul>
11511 * <li>The Record block object</li>
11512 * <li>The "arg" argument from the load function</li>
11513 * <li>A boolean success indicator</li>
11515 * @param {Object} scope The scope in which to call the callback
11516 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11518 load : function(params, reader, callback, scope, arg){
11519 if(this.fireEvent("beforeload", this, params) !== false){
11521 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11523 var url = this.url;
11524 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11526 url += "&_dc=" + (new Date().getTime());
11528 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11531 cb : "stcCallback"+transId,
11532 scriptId : "stcScript"+transId,
11536 callback : callback,
11542 window[trans.cb] = function(o){
11543 conn.handleResponse(o, trans);
11546 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11548 if(this.autoAbort !== false){
11552 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11554 var script = document.createElement("script");
11555 script.setAttribute("src", url);
11556 script.setAttribute("type", "text/javascript");
11557 script.setAttribute("id", trans.scriptId);
11558 this.head.appendChild(script);
11560 this.trans = trans;
11562 callback.call(scope||this, null, arg, false);
11567 isLoading : function(){
11568 return this.trans ? true : false;
11572 * Abort the current server request.
11574 abort : function(){
11575 if(this.isLoading()){
11576 this.destroyTrans(this.trans);
11581 destroyTrans : function(trans, isLoaded){
11582 this.head.removeChild(document.getElementById(trans.scriptId));
11583 clearTimeout(trans.timeoutId);
11585 window[trans.cb] = undefined;
11587 delete window[trans.cb];
11590 // if hasn't been loaded, wait for load to remove it to prevent script error
11591 window[trans.cb] = function(){
11592 window[trans.cb] = undefined;
11594 delete window[trans.cb];
11601 handleResponse : function(o, trans){
11602 this.trans = false;
11603 this.destroyTrans(trans, true);
11606 result = trans.reader.readRecords(o);
11608 this.fireEvent("loadexception", this, o, trans.arg, e);
11609 trans.callback.call(trans.scope||window, null, trans.arg, false);
11612 this.fireEvent("load", this, o, trans.arg);
11613 trans.callback.call(trans.scope||window, result, trans.arg, true);
11617 handleFailure : function(trans){
11618 this.trans = false;
11619 this.destroyTrans(trans, false);
11620 this.fireEvent("loadexception", this, null, trans.arg);
11621 trans.callback.call(trans.scope||window, null, trans.arg, false);
11625 * Ext JS Library 1.1.1
11626 * Copyright(c) 2006-2007, Ext JS, LLC.
11628 * Originally Released Under LGPL - original licence link has changed is not relivant.
11631 * <script type="text/javascript">
11635 * @class Roo.data.JsonReader
11636 * @extends Roo.data.DataReader
11637 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11638 * based on mappings in a provided Roo.data.Record constructor.
11640 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11641 * in the reply previously.
11646 var RecordDef = Roo.data.Record.create([
11647 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11648 {name: 'occupation'} // This field will use "occupation" as the mapping.
11650 var myReader = new Roo.data.JsonReader({
11651 totalProperty: "results", // The property which contains the total dataset size (optional)
11652 root: "rows", // The property which contains an Array of row objects
11653 id: "id" // The property within each row object that provides an ID for the record (optional)
11657 * This would consume a JSON file like this:
11659 { 'results': 2, 'rows': [
11660 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11661 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11664 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11665 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11666 * paged from the remote server.
11667 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11668 * @cfg {String} root name of the property which contains the Array of row objects.
11669 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11670 * @cfg {Array} fields Array of field definition objects
11672 * Create a new JsonReader
11673 * @param {Object} meta Metadata configuration options
11674 * @param {Object} recordType Either an Array of field definition objects,
11675 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11677 Roo.data.JsonReader = function(meta, recordType){
11680 // set some defaults:
11681 Roo.applyIf(meta, {
11682 totalProperty: 'total',
11683 successProperty : 'success',
11688 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11690 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11693 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11694 * Used by Store query builder to append _requestMeta to params.
11697 metaFromRemote : false,
11699 * This method is only used by a DataProxy which has retrieved data from a remote server.
11700 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11701 * @return {Object} data A data block which is used by an Roo.data.Store object as
11702 * a cache of Roo.data.Records.
11704 read : function(response){
11705 var json = response.responseText;
11707 var o = /* eval:var:o */ eval("("+json+")");
11709 throw {message: "JsonReader.read: Json object not found"};
11715 this.metaFromRemote = true;
11716 this.meta = o.metaData;
11717 this.recordType = Roo.data.Record.create(o.metaData.fields);
11718 this.onMetaChange(this.meta, this.recordType, o);
11720 return this.readRecords(o);
11723 // private function a store will implement
11724 onMetaChange : function(meta, recordType, o){
11731 simpleAccess: function(obj, subsc) {
11738 getJsonAccessor: function(){
11740 return function(expr) {
11742 return(re.test(expr))
11743 ? new Function("obj", "return obj." + expr)
11748 return Roo.emptyFn;
11753 * Create a data block containing Roo.data.Records from an XML document.
11754 * @param {Object} o An object which contains an Array of row objects in the property specified
11755 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11756 * which contains the total size of the dataset.
11757 * @return {Object} data A data block which is used by an Roo.data.Store object as
11758 * a cache of Roo.data.Records.
11760 readRecords : function(o){
11762 * After any data loads, the raw JSON data is available for further custom processing.
11766 var s = this.meta, Record = this.recordType,
11767 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11769 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11771 if(s.totalProperty) {
11772 this.getTotal = this.getJsonAccessor(s.totalProperty);
11774 if(s.successProperty) {
11775 this.getSuccess = this.getJsonAccessor(s.successProperty);
11777 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11779 var g = this.getJsonAccessor(s.id);
11780 this.getId = function(rec) {
11782 return (r === undefined || r === "") ? null : r;
11785 this.getId = function(){return null;};
11788 for(var jj = 0; jj < fl; jj++){
11790 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11791 this.ef[jj] = this.getJsonAccessor(map);
11795 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11796 if(s.totalProperty){
11797 var vt = parseInt(this.getTotal(o), 10);
11802 if(s.successProperty){
11803 var vs = this.getSuccess(o);
11804 if(vs === false || vs === 'false'){
11809 for(var i = 0; i < c; i++){
11812 var id = this.getId(n);
11813 for(var j = 0; j < fl; j++){
11815 var v = this.ef[j](n);
11817 Roo.log('missing convert for ' + f.name);
11821 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11823 var record = new Record(values, id);
11825 records[i] = record;
11831 totalRecords : totalRecords
11836 * Ext JS Library 1.1.1
11837 * Copyright(c) 2006-2007, Ext JS, LLC.
11839 * Originally Released Under LGPL - original licence link has changed is not relivant.
11842 * <script type="text/javascript">
11846 * @class Roo.data.ArrayReader
11847 * @extends Roo.data.DataReader
11848 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11849 * Each element of that Array represents a row of data fields. The
11850 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11851 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11855 var RecordDef = Roo.data.Record.create([
11856 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11857 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11859 var myReader = new Roo.data.ArrayReader({
11860 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11864 * This would consume an Array like this:
11866 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11868 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11870 * Create a new JsonReader
11871 * @param {Object} meta Metadata configuration options.
11872 * @param {Object} recordType Either an Array of field definition objects
11873 * as specified to {@link Roo.data.Record#create},
11874 * or an {@link Roo.data.Record} object
11875 * created using {@link Roo.data.Record#create}.
11877 Roo.data.ArrayReader = function(meta, recordType){
11878 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11881 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11883 * Create a data block containing Roo.data.Records from an XML document.
11884 * @param {Object} o An Array of row objects which represents the dataset.
11885 * @return {Object} data A data block which is used by an Roo.data.Store object as
11886 * a cache of Roo.data.Records.
11888 readRecords : function(o){
11889 var sid = this.meta ? this.meta.id : null;
11890 var recordType = this.recordType, fields = recordType.prototype.fields;
11893 for(var i = 0; i < root.length; i++){
11896 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11897 for(var j = 0, jlen = fields.length; j < jlen; j++){
11898 var f = fields.items[j];
11899 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11900 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11902 values[f.name] = v;
11904 var record = new recordType(values, id);
11906 records[records.length] = record;
11910 totalRecords : records.length
11919 * @class Roo.bootstrap.ComboBox
11920 * @extends Roo.bootstrap.TriggerField
11921 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11922 * @cfg {Boolean} append (true|false) default false
11923 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11924 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11925 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11926 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11927 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11928 * @cfg {Boolean} animate default true
11929 * @cfg {Boolean} emptyResultText only for touch device
11930 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11932 * Create a new ComboBox.
11933 * @param {Object} config Configuration options
11935 Roo.bootstrap.ComboBox = function(config){
11936 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11940 * Fires when the dropdown list is expanded
11941 * @param {Roo.bootstrap.ComboBox} combo This combo box
11946 * Fires when the dropdown list is collapsed
11947 * @param {Roo.bootstrap.ComboBox} combo This combo box
11951 * @event beforeselect
11952 * Fires before a list item is selected. Return false to cancel the selection.
11953 * @param {Roo.bootstrap.ComboBox} combo This combo box
11954 * @param {Roo.data.Record} record The data record returned from the underlying store
11955 * @param {Number} index The index of the selected item in the dropdown list
11957 'beforeselect' : true,
11960 * Fires when a list item is selected
11961 * @param {Roo.bootstrap.ComboBox} combo This combo box
11962 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11963 * @param {Number} index The index of the selected item in the dropdown list
11967 * @event beforequery
11968 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11969 * The event object passed has these properties:
11970 * @param {Roo.bootstrap.ComboBox} combo This combo box
11971 * @param {String} query The query
11972 * @param {Boolean} forceAll true to force "all" query
11973 * @param {Boolean} cancel true to cancel the query
11974 * @param {Object} e The query event object
11976 'beforequery': true,
11979 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11980 * @param {Roo.bootstrap.ComboBox} combo This combo box
11985 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11986 * @param {Roo.bootstrap.ComboBox} combo This combo box
11987 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11992 * Fires when the remove value from the combobox array
11993 * @param {Roo.bootstrap.ComboBox} combo This combo box
11997 * @event afterremove
11998 * Fires when the remove value from the combobox array
11999 * @param {Roo.bootstrap.ComboBox} combo This combo box
12001 'afterremove' : true,
12003 * @event specialfilter
12004 * Fires when specialfilter
12005 * @param {Roo.bootstrap.ComboBox} combo This combo box
12007 'specialfilter' : true,
12010 * Fires when tick the element
12011 * @param {Roo.bootstrap.ComboBox} combo This combo box
12015 * @event touchviewdisplay
12016 * Fires when touch view require special display (default is using displayField)
12017 * @param {Roo.bootstrap.ComboBox} combo This combo box
12018 * @param {Object} cfg set html .
12020 'touchviewdisplay' : true
12025 this.tickItems = [];
12027 this.selectedIndex = -1;
12028 if(this.mode == 'local'){
12029 if(config.queryDelay === undefined){
12030 this.queryDelay = 10;
12032 if(config.minChars === undefined){
12038 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12041 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12042 * rendering into an Roo.Editor, defaults to false)
12045 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12046 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12049 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12052 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12053 * the dropdown list (defaults to undefined, with no header element)
12057 * @cfg {String/Roo.Template} tpl The template to use to render the output
12061 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12063 listWidth: undefined,
12065 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12066 * mode = 'remote' or 'text' if mode = 'local')
12068 displayField: undefined,
12071 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12072 * mode = 'remote' or 'value' if mode = 'local').
12073 * Note: use of a valueField requires the user make a selection
12074 * in order for a value to be mapped.
12076 valueField: undefined,
12078 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12083 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12084 * field's data value (defaults to the underlying DOM element's name)
12086 hiddenName: undefined,
12088 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12092 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12094 selectedClass: 'active',
12097 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12101 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12102 * anchor positions (defaults to 'tl-bl')
12104 listAlign: 'tl-bl?',
12106 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12110 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12111 * query specified by the allQuery config option (defaults to 'query')
12113 triggerAction: 'query',
12115 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12116 * (defaults to 4, does not apply if editable = false)
12120 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12121 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12125 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12126 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12130 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12131 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12135 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12136 * when editable = true (defaults to false)
12138 selectOnFocus:false,
12140 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12142 queryParam: 'query',
12144 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12145 * when mode = 'remote' (defaults to 'Loading...')
12147 loadingText: 'Loading...',
12149 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12153 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12157 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12158 * traditional select (defaults to true)
12162 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12166 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12170 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12171 * listWidth has a higher value)
12175 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12176 * allow the user to set arbitrary text into the field (defaults to false)
12178 forceSelection:false,
12180 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12181 * if typeAhead = true (defaults to 250)
12183 typeAheadDelay : 250,
12185 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12186 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12188 valueNotFoundText : undefined,
12190 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12192 blockFocus : false,
12195 * @cfg {Boolean} disableClear Disable showing of clear button.
12197 disableClear : false,
12199 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12201 alwaysQuery : false,
12204 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12209 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12211 invalidClass : "has-warning",
12214 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12216 validClass : "has-success",
12219 * @cfg {Boolean} specialFilter (true|false) special filter default false
12221 specialFilter : false,
12224 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12226 mobileTouchView : true,
12238 btnPosition : 'right',
12239 triggerList : true,
12240 showToggleBtn : true,
12242 emptyResultText: 'Empty',
12243 triggerText : 'Select',
12245 // element that contains real text value.. (when hidden is used..)
12247 getAutoCreate : function()
12255 if(Roo.isTouch && this.mobileTouchView){
12256 cfg = this.getAutoCreateTouchView();
12263 if(!this.tickable){
12264 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12269 * ComboBox with tickable selections
12272 var align = this.labelAlign || this.parentLabelAlign();
12275 cls : 'form-group roo-combobox-tickable' //input-group
12280 cls : 'tickable-buttons',
12285 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12286 html : this.triggerText
12292 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12299 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12306 buttons.cn.unshift({
12308 cls: 'roo-select2-search-field-input'
12314 Roo.each(buttons.cn, function(c){
12316 c.cls += ' btn-' + _this.size;
12319 if (_this.disabled) {
12330 cls: 'form-hidden-field'
12334 cls: 'roo-select2-choices',
12338 cls: 'roo-select2-search-field',
12350 cls: 'roo-select2-container input-group roo-select2-container-multi',
12355 // cls: 'typeahead typeahead-long dropdown-menu',
12356 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12361 if(this.hasFeedback && !this.allowBlank){
12365 cls: 'glyphicon form-control-feedback'
12368 combobox.cn.push(feedback);
12371 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12373 // Roo.log("left and has label");
12377 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12378 tooltip : 'This field is required'
12383 cls : 'control-label col-sm-' + this.labelWidth,
12384 html : this.fieldLabel
12388 cls : "col-sm-" + (12 - this.labelWidth),
12396 if(this.indicatorpos == 'right'){
12402 cls : 'control-label col-sm-' + this.labelWidth,
12403 html : this.fieldLabel
12408 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12409 tooltip : 'This field is required'
12412 cls : "col-sm-" + (12 - this.labelWidth),
12423 } else if ( this.fieldLabel.length) {
12424 // Roo.log(" label");
12428 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12429 tooltip : 'This field is required'
12433 //cls : 'input-group-addon',
12434 html : this.fieldLabel
12442 if(this.indicatorpos == 'right'){
12447 //cls : 'input-group-addon',
12448 html : this.fieldLabel
12454 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12455 tooltip : 'This field is required'
12466 // Roo.log(" no label && no align");
12473 ['xs','sm','md','lg'].map(function(size){
12474 if (settings[size]) {
12475 cfg.cls += ' col-' + size + '-' + settings[size];
12483 _initEventsCalled : false,
12486 initEvents: function()
12489 if (this._initEventsCalled) { // as we call render... prevent looping...
12492 this._initEventsCalled = true;
12495 throw "can not find store for combo";
12498 this.store = Roo.factory(this.store, Roo.data);
12500 // if we are building from html. then this element is so complex, that we can not really
12501 // use the rendered HTML.
12502 // so we have to trash and replace the previous code.
12503 if (Roo.XComponent.build_from_html) {
12505 // remove this element....
12506 var e = this.el.dom, k=0;
12507 while (e ) { e = e.previousSibling; ++k;}
12512 this.rendered = false;
12514 this.render(this.parent().getChildContainer(true), k);
12525 if(Roo.isTouch && this.mobileTouchView){
12526 this.initTouchView();
12531 this.initTickableEvents();
12535 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12537 if(this.hiddenName){
12539 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12541 this.hiddenField.dom.value =
12542 this.hiddenValue !== undefined ? this.hiddenValue :
12543 this.value !== undefined ? this.value : '';
12545 // prevent input submission
12546 this.el.dom.removeAttribute('name');
12547 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12552 // this.el.dom.setAttribute('autocomplete', 'off');
12555 var cls = 'x-combo-list';
12557 //this.list = new Roo.Layer({
12558 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12564 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12565 _this.list.setWidth(lw);
12568 this.list.on('mouseover', this.onViewOver, this);
12569 this.list.on('mousemove', this.onViewMove, this);
12571 this.list.on('scroll', this.onViewScroll, this);
12574 this.list.swallowEvent('mousewheel');
12575 this.assetHeight = 0;
12578 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12579 this.assetHeight += this.header.getHeight();
12582 this.innerList = this.list.createChild({cls:cls+'-inner'});
12583 this.innerList.on('mouseover', this.onViewOver, this);
12584 this.innerList.on('mousemove', this.onViewMove, this);
12585 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12587 if(this.allowBlank && !this.pageSize && !this.disableClear){
12588 this.footer = this.list.createChild({cls:cls+'-ft'});
12589 this.pageTb = new Roo.Toolbar(this.footer);
12593 this.footer = this.list.createChild({cls:cls+'-ft'});
12594 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12595 {pageSize: this.pageSize});
12599 if (this.pageTb && this.allowBlank && !this.disableClear) {
12601 this.pageTb.add(new Roo.Toolbar.Fill(), {
12602 cls: 'x-btn-icon x-btn-clear',
12604 handler: function()
12607 _this.clearValue();
12608 _this.onSelect(false, -1);
12613 this.assetHeight += this.footer.getHeight();
12618 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12621 this.view = new Roo.View(this.list, this.tpl, {
12622 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12624 //this.view.wrapEl.setDisplayed(false);
12625 this.view.on('click', this.onViewClick, this);
12629 this.store.on('beforeload', this.onBeforeLoad, this);
12630 this.store.on('load', this.onLoad, this);
12631 this.store.on('loadexception', this.onLoadException, this);
12633 if(this.resizable){
12634 this.resizer = new Roo.Resizable(this.list, {
12635 pinned:true, handles:'se'
12637 this.resizer.on('resize', function(r, w, h){
12638 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12639 this.listWidth = w;
12640 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12641 this.restrictHeight();
12643 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12646 if(!this.editable){
12647 this.editable = true;
12648 this.setEditable(false);
12653 if (typeof(this.events.add.listeners) != 'undefined') {
12655 this.addicon = this.wrap.createChild(
12656 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12658 this.addicon.on('click', function(e) {
12659 this.fireEvent('add', this);
12662 if (typeof(this.events.edit.listeners) != 'undefined') {
12664 this.editicon = this.wrap.createChild(
12665 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12666 if (this.addicon) {
12667 this.editicon.setStyle('margin-left', '40px');
12669 this.editicon.on('click', function(e) {
12671 // we fire even if inothing is selected..
12672 this.fireEvent('edit', this, this.lastData );
12678 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12679 "up" : function(e){
12680 this.inKeyMode = true;
12684 "down" : function(e){
12685 if(!this.isExpanded()){
12686 this.onTriggerClick();
12688 this.inKeyMode = true;
12693 "enter" : function(e){
12694 // this.onViewClick();
12698 if(this.fireEvent("specialkey", this, e)){
12699 this.onViewClick(false);
12705 "esc" : function(e){
12709 "tab" : function(e){
12712 if(this.fireEvent("specialkey", this, e)){
12713 this.onViewClick(false);
12721 doRelay : function(foo, bar, hname){
12722 if(hname == 'down' || this.scope.isExpanded()){
12723 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12732 this.queryDelay = Math.max(this.queryDelay || 10,
12733 this.mode == 'local' ? 10 : 250);
12736 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12738 if(this.typeAhead){
12739 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12741 if(this.editable !== false){
12742 this.inputEl().on("keyup", this.onKeyUp, this);
12744 if(this.forceSelection){
12745 this.inputEl().on('blur', this.doForce, this);
12749 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12750 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12754 initTickableEvents: function()
12758 if(this.hiddenName){
12760 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12762 this.hiddenField.dom.value =
12763 this.hiddenValue !== undefined ? this.hiddenValue :
12764 this.value !== undefined ? this.value : '';
12766 // prevent input submission
12767 this.el.dom.removeAttribute('name');
12768 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12773 // this.list = this.el.select('ul.dropdown-menu',true).first();
12775 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12776 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12777 if(this.triggerList){
12778 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12781 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12782 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12784 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12785 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12787 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12788 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12790 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12791 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12792 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12795 this.cancelBtn.hide();
12800 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12801 _this.list.setWidth(lw);
12804 this.list.on('mouseover', this.onViewOver, this);
12805 this.list.on('mousemove', this.onViewMove, this);
12807 this.list.on('scroll', this.onViewScroll, this);
12810 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>';
12813 this.view = new Roo.View(this.list, this.tpl, {
12814 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12817 //this.view.wrapEl.setDisplayed(false);
12818 this.view.on('click', this.onViewClick, this);
12822 this.store.on('beforeload', this.onBeforeLoad, this);
12823 this.store.on('load', this.onLoad, this);
12824 this.store.on('loadexception', this.onLoadException, this);
12827 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12828 "up" : function(e){
12829 this.inKeyMode = true;
12833 "down" : function(e){
12834 this.inKeyMode = true;
12838 "enter" : function(e){
12839 if(this.fireEvent("specialkey", this, e)){
12840 this.onViewClick(false);
12846 "esc" : function(e){
12847 this.onTickableFooterButtonClick(e, false, false);
12850 "tab" : function(e){
12851 this.fireEvent("specialkey", this, e);
12853 this.onTickableFooterButtonClick(e, false, false);
12860 doRelay : function(e, fn, key){
12861 if(this.scope.isExpanded()){
12862 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12871 this.queryDelay = Math.max(this.queryDelay || 10,
12872 this.mode == 'local' ? 10 : 250);
12875 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12877 if(this.typeAhead){
12878 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12881 if(this.editable !== false){
12882 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12887 onDestroy : function(){
12889 this.view.setStore(null);
12890 this.view.el.removeAllListeners();
12891 this.view.el.remove();
12892 this.view.purgeListeners();
12895 this.list.dom.innerHTML = '';
12899 this.store.un('beforeload', this.onBeforeLoad, this);
12900 this.store.un('load', this.onLoad, this);
12901 this.store.un('loadexception', this.onLoadException, this);
12903 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12907 fireKey : function(e){
12908 if(e.isNavKeyPress() && !this.list.isVisible()){
12909 this.fireEvent("specialkey", this, e);
12914 onResize: function(w, h){
12915 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12917 // if(typeof w != 'number'){
12918 // // we do not handle it!?!?
12921 // var tw = this.trigger.getWidth();
12922 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12923 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12925 // this.inputEl().setWidth( this.adjustWidth('input', x));
12927 // //this.trigger.setStyle('left', x+'px');
12929 // if(this.list && this.listWidth === undefined){
12930 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12931 // this.list.setWidth(lw);
12932 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12940 * Allow or prevent the user from directly editing the field text. If false is passed,
12941 * the user will only be able to select from the items defined in the dropdown list. This method
12942 * is the runtime equivalent of setting the 'editable' config option at config time.
12943 * @param {Boolean} value True to allow the user to directly edit the field text
12945 setEditable : function(value){
12946 if(value == this.editable){
12949 this.editable = value;
12951 this.inputEl().dom.setAttribute('readOnly', true);
12952 this.inputEl().on('mousedown', this.onTriggerClick, this);
12953 this.inputEl().addClass('x-combo-noedit');
12955 this.inputEl().dom.setAttribute('readOnly', false);
12956 this.inputEl().un('mousedown', this.onTriggerClick, this);
12957 this.inputEl().removeClass('x-combo-noedit');
12963 onBeforeLoad : function(combo,opts){
12964 if(!this.hasFocus){
12968 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12970 this.restrictHeight();
12971 this.selectedIndex = -1;
12975 onLoad : function(){
12977 this.hasQuery = false;
12979 if(!this.hasFocus){
12983 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12984 this.loading.hide();
12987 if(this.store.getCount() > 0){
12989 this.restrictHeight();
12990 if(this.lastQuery == this.allQuery){
12991 if(this.editable && !this.tickable){
12992 this.inputEl().dom.select();
12996 !this.selectByValue(this.value, true) &&
12999 !this.store.lastOptions ||
13000 typeof(this.store.lastOptions.add) == 'undefined' ||
13001 this.store.lastOptions.add != true
13004 this.select(0, true);
13007 if(this.autoFocus){
13010 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13011 this.taTask.delay(this.typeAheadDelay);
13015 this.onEmptyResults();
13021 onLoadException : function()
13023 this.hasQuery = false;
13025 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13026 this.loading.hide();
13029 if(this.tickable && this.editable){
13034 // only causes errors at present
13035 //Roo.log(this.store.reader.jsonData);
13036 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13038 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13044 onTypeAhead : function(){
13045 if(this.store.getCount() > 0){
13046 var r = this.store.getAt(0);
13047 var newValue = r.data[this.displayField];
13048 var len = newValue.length;
13049 var selStart = this.getRawValue().length;
13051 if(selStart != len){
13052 this.setRawValue(newValue);
13053 this.selectText(selStart, newValue.length);
13059 onSelect : function(record, index){
13061 if(this.fireEvent('beforeselect', this, record, index) !== false){
13063 this.setFromData(index > -1 ? record.data : false);
13066 this.fireEvent('select', this, record, index);
13071 * Returns the currently selected field value or empty string if no value is set.
13072 * @return {String} value The selected value
13074 getValue : function(){
13077 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13080 if(this.valueField){
13081 return typeof this.value != 'undefined' ? this.value : '';
13083 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13088 * Clears any text/value currently set in the field
13090 clearValue : function(){
13091 if(this.hiddenField){
13092 this.hiddenField.dom.value = '';
13095 this.setRawValue('');
13096 this.lastSelectionText = '';
13097 this.lastData = false;
13099 var close = this.closeTriggerEl();
13110 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13111 * will be displayed in the field. If the value does not match the data value of an existing item,
13112 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13113 * Otherwise the field will be blank (although the value will still be set).
13114 * @param {String} value The value to match
13116 setValue : function(v){
13123 if(this.valueField){
13124 var r = this.findRecord(this.valueField, v);
13126 text = r.data[this.displayField];
13127 }else if(this.valueNotFoundText !== undefined){
13128 text = this.valueNotFoundText;
13131 this.lastSelectionText = text;
13132 if(this.hiddenField){
13133 this.hiddenField.dom.value = v;
13135 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13138 var close = this.closeTriggerEl();
13141 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13147 * @property {Object} the last set data for the element
13152 * Sets the value of the field based on a object which is related to the record format for the store.
13153 * @param {Object} value the value to set as. or false on reset?
13155 setFromData : function(o){
13162 var dv = ''; // display value
13163 var vv = ''; // value value..
13165 if (this.displayField) {
13166 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13168 // this is an error condition!!!
13169 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13172 if(this.valueField){
13173 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13176 var close = this.closeTriggerEl();
13179 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13182 if(this.hiddenField){
13183 this.hiddenField.dom.value = vv;
13185 this.lastSelectionText = dv;
13186 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13190 // no hidden field.. - we store the value in 'value', but still display
13191 // display field!!!!
13192 this.lastSelectionText = dv;
13193 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13200 reset : function(){
13201 // overridden so that last data is reset..
13208 this.setValue(this.originalValue);
13209 //this.clearInvalid();
13210 this.lastData = false;
13212 this.view.clearSelections();
13218 findRecord : function(prop, value){
13220 if(this.store.getCount() > 0){
13221 this.store.each(function(r){
13222 if(r.data[prop] == value){
13232 getName: function()
13234 // returns hidden if it's set..
13235 if (!this.rendered) {return ''};
13236 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13240 onViewMove : function(e, t){
13241 this.inKeyMode = false;
13245 onViewOver : function(e, t){
13246 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13249 var item = this.view.findItemFromChild(t);
13252 var index = this.view.indexOf(item);
13253 this.select(index, false);
13258 onViewClick : function(view, doFocus, el, e)
13260 var index = this.view.getSelectedIndexes()[0];
13262 var r = this.store.getAt(index);
13266 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13273 Roo.each(this.tickItems, function(v,k){
13275 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13277 _this.tickItems.splice(k, 1);
13279 if(typeof(e) == 'undefined' && view == false){
13280 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13292 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13293 this.tickItems.push(r.data);
13296 if(typeof(e) == 'undefined' && view == false){
13297 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13304 this.onSelect(r, index);
13306 if(doFocus !== false && !this.blockFocus){
13307 this.inputEl().focus();
13312 restrictHeight : function(){
13313 //this.innerList.dom.style.height = '';
13314 //var inner = this.innerList.dom;
13315 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13316 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13317 //this.list.beginUpdate();
13318 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13319 this.list.alignTo(this.inputEl(), this.listAlign);
13320 this.list.alignTo(this.inputEl(), this.listAlign);
13321 //this.list.endUpdate();
13325 onEmptyResults : function(){
13327 if(this.tickable && this.editable){
13328 this.restrictHeight();
13336 * Returns true if the dropdown list is expanded, else false.
13338 isExpanded : function(){
13339 return this.list.isVisible();
13343 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13344 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13345 * @param {String} value The data value of the item to select
13346 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13347 * selected item if it is not currently in view (defaults to true)
13348 * @return {Boolean} True if the value matched an item in the list, else false
13350 selectByValue : function(v, scrollIntoView){
13351 if(v !== undefined && v !== null){
13352 var r = this.findRecord(this.valueField || this.displayField, v);
13354 this.select(this.store.indexOf(r), scrollIntoView);
13362 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13363 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13364 * @param {Number} index The zero-based index of the list item to select
13365 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13366 * selected item if it is not currently in view (defaults to true)
13368 select : function(index, scrollIntoView){
13369 this.selectedIndex = index;
13370 this.view.select(index);
13371 if(scrollIntoView !== false){
13372 var el = this.view.getNode(index);
13374 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13377 this.list.scrollChildIntoView(el, false);
13383 selectNext : function(){
13384 var ct = this.store.getCount();
13386 if(this.selectedIndex == -1){
13388 }else if(this.selectedIndex < ct-1){
13389 this.select(this.selectedIndex+1);
13395 selectPrev : function(){
13396 var ct = this.store.getCount();
13398 if(this.selectedIndex == -1){
13400 }else if(this.selectedIndex != 0){
13401 this.select(this.selectedIndex-1);
13407 onKeyUp : function(e){
13408 if(this.editable !== false && !e.isSpecialKey()){
13409 this.lastKey = e.getKey();
13410 this.dqTask.delay(this.queryDelay);
13415 validateBlur : function(){
13416 return !this.list || !this.list.isVisible();
13420 initQuery : function(){
13422 var v = this.getRawValue();
13424 if(this.tickable && this.editable){
13425 v = this.tickableInputEl().getValue();
13432 doForce : function(){
13433 if(this.inputEl().dom.value.length > 0){
13434 this.inputEl().dom.value =
13435 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13441 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13442 * query allowing the query action to be canceled if needed.
13443 * @param {String} query The SQL query to execute
13444 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13445 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13446 * saved in the current store (defaults to false)
13448 doQuery : function(q, forceAll){
13450 if(q === undefined || q === null){
13455 forceAll: forceAll,
13459 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13464 forceAll = qe.forceAll;
13465 if(forceAll === true || (q.length >= this.minChars)){
13467 this.hasQuery = true;
13469 if(this.lastQuery != q || this.alwaysQuery){
13470 this.lastQuery = q;
13471 if(this.mode == 'local'){
13472 this.selectedIndex = -1;
13474 this.store.clearFilter();
13477 if(this.specialFilter){
13478 this.fireEvent('specialfilter', this);
13483 this.store.filter(this.displayField, q);
13486 this.store.fireEvent("datachanged", this.store);
13493 this.store.baseParams[this.queryParam] = q;
13495 var options = {params : this.getParams(q)};
13498 options.add = true;
13499 options.params.start = this.page * this.pageSize;
13502 this.store.load(options);
13505 * this code will make the page width larger, at the beginning, the list not align correctly,
13506 * we should expand the list on onLoad
13507 * so command out it
13512 this.selectedIndex = -1;
13517 this.loadNext = false;
13521 getParams : function(q){
13523 //p[this.queryParam] = q;
13527 p.limit = this.pageSize;
13533 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13535 collapse : function(){
13536 if(!this.isExpanded()){
13543 this.hasFocus = false;
13545 this.cancelBtn.hide();
13546 this.trigger.show();
13549 this.tickableInputEl().dom.value = '';
13550 this.tickableInputEl().blur();
13555 Roo.get(document).un('mousedown', this.collapseIf, this);
13556 Roo.get(document).un('mousewheel', this.collapseIf, this);
13557 if (!this.editable) {
13558 Roo.get(document).un('keydown', this.listKeyPress, this);
13560 this.fireEvent('collapse', this);
13566 collapseIf : function(e){
13567 var in_combo = e.within(this.el);
13568 var in_list = e.within(this.list);
13569 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13571 if (in_combo || in_list || is_list) {
13572 //e.stopPropagation();
13577 this.onTickableFooterButtonClick(e, false, false);
13585 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13587 expand : function(){
13589 if(this.isExpanded() || !this.hasFocus){
13593 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13594 this.list.setWidth(lw);
13601 this.restrictHeight();
13605 this.tickItems = Roo.apply([], this.item);
13608 this.cancelBtn.show();
13609 this.trigger.hide();
13612 this.tickableInputEl().focus();
13617 Roo.get(document).on('mousedown', this.collapseIf, this);
13618 Roo.get(document).on('mousewheel', this.collapseIf, this);
13619 if (!this.editable) {
13620 Roo.get(document).on('keydown', this.listKeyPress, this);
13623 this.fireEvent('expand', this);
13627 // Implements the default empty TriggerField.onTriggerClick function
13628 onTriggerClick : function(e)
13630 Roo.log('trigger click');
13632 if(this.disabled || !this.triggerList){
13637 this.loadNext = false;
13639 if(this.isExpanded()){
13641 if (!this.blockFocus) {
13642 this.inputEl().focus();
13646 this.hasFocus = true;
13647 if(this.triggerAction == 'all') {
13648 this.doQuery(this.allQuery, true);
13650 this.doQuery(this.getRawValue());
13652 if (!this.blockFocus) {
13653 this.inputEl().focus();
13658 onTickableTriggerClick : function(e)
13665 this.loadNext = false;
13666 this.hasFocus = true;
13668 if(this.triggerAction == 'all') {
13669 this.doQuery(this.allQuery, true);
13671 this.doQuery(this.getRawValue());
13675 onSearchFieldClick : function(e)
13677 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13678 this.onTickableFooterButtonClick(e, false, false);
13682 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13687 this.loadNext = false;
13688 this.hasFocus = true;
13690 if(this.triggerAction == 'all') {
13691 this.doQuery(this.allQuery, true);
13693 this.doQuery(this.getRawValue());
13697 listKeyPress : function(e)
13699 //Roo.log('listkeypress');
13700 // scroll to first matching element based on key pres..
13701 if (e.isSpecialKey()) {
13704 var k = String.fromCharCode(e.getKey()).toUpperCase();
13707 var csel = this.view.getSelectedNodes();
13708 var cselitem = false;
13710 var ix = this.view.indexOf(csel[0]);
13711 cselitem = this.store.getAt(ix);
13712 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13718 this.store.each(function(v) {
13720 // start at existing selection.
13721 if (cselitem.id == v.id) {
13727 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13728 match = this.store.indexOf(v);
13734 if (match === false) {
13735 return true; // no more action?
13738 this.view.select(match);
13739 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13740 sn.scrollIntoView(sn.dom.parentNode, false);
13743 onViewScroll : function(e, t){
13745 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){
13749 this.hasQuery = true;
13751 this.loading = this.list.select('.loading', true).first();
13753 if(this.loading === null){
13754 this.list.createChild({
13756 cls: 'loading roo-select2-more-results roo-select2-active',
13757 html: 'Loading more results...'
13760 this.loading = this.list.select('.loading', true).first();
13762 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13764 this.loading.hide();
13767 this.loading.show();
13772 this.loadNext = true;
13774 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13779 addItem : function(o)
13781 var dv = ''; // display value
13783 if (this.displayField) {
13784 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13786 // this is an error condition!!!
13787 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13794 var choice = this.choices.createChild({
13796 cls: 'roo-select2-search-choice',
13805 cls: 'roo-select2-search-choice-close',
13810 }, this.searchField);
13812 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13814 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13822 this.inputEl().dom.value = '';
13827 onRemoveItem : function(e, _self, o)
13829 e.preventDefault();
13831 this.lastItem = Roo.apply([], this.item);
13833 var index = this.item.indexOf(o.data) * 1;
13836 Roo.log('not this item?!');
13840 this.item.splice(index, 1);
13845 this.fireEvent('remove', this, e);
13851 syncValue : function()
13853 if(!this.item.length){
13860 Roo.each(this.item, function(i){
13861 if(_this.valueField){
13862 value.push(i[_this.valueField]);
13869 this.value = value.join(',');
13871 if(this.hiddenField){
13872 this.hiddenField.dom.value = this.value;
13875 this.store.fireEvent("datachanged", this.store);
13880 clearItem : function()
13882 if(!this.multiple){
13888 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13896 if(this.tickable && !Roo.isTouch){
13897 this.view.refresh();
13901 inputEl: function ()
13903 if(Roo.isTouch && this.mobileTouchView){
13904 return this.el.select('input.form-control',true).first();
13908 return this.searchField;
13911 return this.el.select('input.form-control',true).first();
13915 onTickableFooterButtonClick : function(e, btn, el)
13917 e.preventDefault();
13919 this.lastItem = Roo.apply([], this.item);
13921 if(btn && btn.name == 'cancel'){
13922 this.tickItems = Roo.apply([], this.item);
13931 Roo.each(this.tickItems, function(o){
13939 validate : function()
13941 var v = this.getRawValue();
13944 v = this.getValue();
13947 if(this.disabled || this.allowBlank || v.length){
13952 this.markInvalid();
13956 tickableInputEl : function()
13958 if(!this.tickable || !this.editable){
13959 return this.inputEl();
13962 return this.inputEl().select('.roo-select2-search-field-input', true).first();
13966 getAutoCreateTouchView : function()
13971 cls: 'form-group' //input-group
13977 type : this.inputType,
13978 cls : 'form-control x-combo-noedit',
13979 autocomplete: 'new-password',
13980 placeholder : this.placeholder || '',
13985 input.name = this.name;
13989 input.cls += ' input-' + this.size;
13992 if (this.disabled) {
13993 input.disabled = true;
14004 inputblock.cls += ' input-group';
14006 inputblock.cn.unshift({
14008 cls : 'input-group-addon',
14013 if(this.removable && !this.multiple){
14014 inputblock.cls += ' roo-removable';
14016 inputblock.cn.push({
14019 cls : 'roo-combo-removable-btn close'
14023 if(this.hasFeedback && !this.allowBlank){
14025 inputblock.cls += ' has-feedback';
14027 inputblock.cn.push({
14029 cls: 'glyphicon form-control-feedback'
14036 inputblock.cls += (this.before) ? '' : ' input-group';
14038 inputblock.cn.push({
14040 cls : 'input-group-addon',
14051 cls: 'form-hidden-field'
14065 cls: 'form-hidden-field'
14069 cls: 'roo-select2-choices',
14073 cls: 'roo-select2-search-field',
14086 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14092 if(!this.multiple && this.showToggleBtn){
14099 if (this.caret != false) {
14102 cls: 'fa fa-' + this.caret
14109 cls : 'input-group-addon btn dropdown-toggle',
14114 cls: 'combobox-clear',
14128 combobox.cls += ' roo-select2-container-multi';
14131 var align = this.labelAlign || this.parentLabelAlign();
14135 if(this.fieldLabel.length && this.labelWidth){
14137 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14138 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14143 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14144 tooltip : 'This field is required'
14148 cls : 'control-label ' + lw,
14149 html : this.fieldLabel
14160 if(this.indicatorpos == 'right'){
14164 cls : 'control-label ' + lw,
14165 html : this.fieldLabel
14170 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14171 tooltip : 'This field is required'
14183 var settings = this;
14185 ['xs','sm','md','lg'].map(function(size){
14186 if (settings[size]) {
14187 cfg.cls += ' col-' + size + '-' + settings[size];
14194 initTouchView : function()
14196 this.renderTouchView();
14198 this.touchViewEl.on('scroll', function(){
14199 this.el.dom.scrollTop = 0;
14202 this.originalValue = this.getValue();
14204 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14206 this.inputEl().on("click", this.showTouchView, this);
14207 this.triggerEl.on("click", this.showTouchView, this);
14209 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14210 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14212 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14214 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14215 this.store.on('load', this.onTouchViewLoad, this);
14216 this.store.on('loadexception', this.onTouchViewLoadException, this);
14218 if(this.hiddenName){
14220 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14222 this.hiddenField.dom.value =
14223 this.hiddenValue !== undefined ? this.hiddenValue :
14224 this.value !== undefined ? this.value : '';
14226 this.el.dom.removeAttribute('name');
14227 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14231 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14232 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14235 if(this.removable && !this.multiple){
14236 var close = this.closeTriggerEl();
14238 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14239 close.on('click', this.removeBtnClick, this, close);
14243 * fix the bug in Safari iOS8
14245 this.inputEl().on("focus", function(e){
14246 document.activeElement.blur();
14254 renderTouchView : function()
14256 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14257 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14259 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14260 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14262 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14263 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14264 this.touchViewBodyEl.setStyle('overflow', 'auto');
14266 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14267 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14269 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14270 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14274 showTouchView : function()
14280 this.touchViewHeaderEl.hide();
14282 if(this.modalTitle.length){
14283 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14284 this.touchViewHeaderEl.show();
14287 this.touchViewEl.show();
14289 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14290 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14291 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14293 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14295 if(this.modalTitle.length){
14296 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14299 this.touchViewBodyEl.setHeight(bodyHeight);
14303 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14305 this.touchViewEl.addClass('in');
14308 this.doTouchViewQuery();
14312 hideTouchView : function()
14314 this.touchViewEl.removeClass('in');
14318 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14320 this.touchViewEl.setStyle('display', 'none');
14325 setTouchViewValue : function()
14332 Roo.each(this.tickItems, function(o){
14337 this.hideTouchView();
14340 doTouchViewQuery : function()
14349 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14353 if(!this.alwaysQuery || this.mode == 'local'){
14354 this.onTouchViewLoad();
14361 onTouchViewBeforeLoad : function(combo,opts)
14367 onTouchViewLoad : function()
14369 if(this.store.getCount() < 1){
14370 this.onTouchViewEmptyResults();
14374 this.clearTouchView();
14376 var rawValue = this.getRawValue();
14378 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14380 this.tickItems = [];
14382 this.store.data.each(function(d, rowIndex){
14383 var row = this.touchViewListGroup.createChild(template);
14385 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14386 row.addClass(d.data.cls);
14389 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14392 html : d.data[this.displayField]
14395 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14396 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14399 row.removeClass('selected');
14400 if(!this.multiple && this.valueField &&
14401 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14404 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14405 row.addClass('selected');
14408 if(this.multiple && this.valueField &&
14409 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14413 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14414 this.tickItems.push(d.data);
14417 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14421 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14423 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14425 if(this.modalTitle.length){
14426 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14429 var listHeight = this.touchViewListGroup.getHeight();
14433 if(firstChecked && listHeight > bodyHeight){
14434 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14439 onTouchViewLoadException : function()
14441 this.hideTouchView();
14444 onTouchViewEmptyResults : function()
14446 this.clearTouchView();
14448 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14450 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14454 clearTouchView : function()
14456 this.touchViewListGroup.dom.innerHTML = '';
14459 onTouchViewClick : function(e, el, o)
14461 e.preventDefault();
14464 var rowIndex = o.rowIndex;
14466 var r = this.store.getAt(rowIndex);
14468 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14470 if(!this.multiple){
14471 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14472 c.dom.removeAttribute('checked');
14475 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14477 this.setFromData(r.data);
14479 var close = this.closeTriggerEl();
14485 this.hideTouchView();
14487 this.fireEvent('select', this, r, rowIndex);
14492 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14493 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14494 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14498 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14499 this.addItem(r.data);
14500 this.tickItems.push(r.data);
14506 * @cfg {Boolean} grow
14510 * @cfg {Number} growMin
14514 * @cfg {Number} growMax
14523 Roo.apply(Roo.bootstrap.ComboBox, {
14527 cls: 'modal-header',
14549 cls: 'list-group-item',
14553 cls: 'roo-combobox-list-group-item-value'
14557 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14571 listItemCheckbox : {
14573 cls: 'list-group-item',
14577 cls: 'roo-combobox-list-group-item-value'
14581 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14597 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14602 cls: 'modal-footer',
14610 cls: 'col-xs-6 text-left',
14613 cls: 'btn btn-danger roo-touch-view-cancel',
14619 cls: 'col-xs-6 text-right',
14622 cls: 'btn btn-success roo-touch-view-ok',
14633 Roo.apply(Roo.bootstrap.ComboBox, {
14635 touchViewTemplate : {
14637 cls: 'modal fade roo-combobox-touch-view',
14641 cls: 'modal-dialog',
14642 style : 'position:fixed', // we have to fix position....
14646 cls: 'modal-content',
14648 Roo.bootstrap.ComboBox.header,
14649 Roo.bootstrap.ComboBox.body,
14650 Roo.bootstrap.ComboBox.footer
14659 * Ext JS Library 1.1.1
14660 * Copyright(c) 2006-2007, Ext JS, LLC.
14662 * Originally Released Under LGPL - original licence link has changed is not relivant.
14665 * <script type="text/javascript">
14670 * @extends Roo.util.Observable
14671 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14672 * This class also supports single and multi selection modes. <br>
14673 * Create a data model bound view:
14675 var store = new Roo.data.Store(...);
14677 var view = new Roo.View({
14679 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14681 singleSelect: true,
14682 selectedClass: "ydataview-selected",
14686 // listen for node click?
14687 view.on("click", function(vw, index, node, e){
14688 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14692 dataModel.load("foobar.xml");
14694 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14696 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14697 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14699 * Note: old style constructor is still suported (container, template, config)
14702 * Create a new View
14703 * @param {Object} config The config object
14706 Roo.View = function(config, depreciated_tpl, depreciated_config){
14708 this.parent = false;
14710 if (typeof(depreciated_tpl) == 'undefined') {
14711 // new way.. - universal constructor.
14712 Roo.apply(this, config);
14713 this.el = Roo.get(this.el);
14716 this.el = Roo.get(config);
14717 this.tpl = depreciated_tpl;
14718 Roo.apply(this, depreciated_config);
14720 this.wrapEl = this.el.wrap().wrap();
14721 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14724 if(typeof(this.tpl) == "string"){
14725 this.tpl = new Roo.Template(this.tpl);
14727 // support xtype ctors..
14728 this.tpl = new Roo.factory(this.tpl, Roo);
14732 this.tpl.compile();
14737 * @event beforeclick
14738 * Fires before a click is processed. Returns false to cancel the default action.
14739 * @param {Roo.View} this
14740 * @param {Number} index The index of the target node
14741 * @param {HTMLElement} node The target node
14742 * @param {Roo.EventObject} e The raw event object
14744 "beforeclick" : true,
14747 * Fires when a template node is clicked.
14748 * @param {Roo.View} this
14749 * @param {Number} index The index of the target node
14750 * @param {HTMLElement} node The target node
14751 * @param {Roo.EventObject} e The raw event object
14756 * Fires when a template node is double clicked.
14757 * @param {Roo.View} this
14758 * @param {Number} index The index of the target node
14759 * @param {HTMLElement} node The target node
14760 * @param {Roo.EventObject} e The raw event object
14764 * @event contextmenu
14765 * Fires when a template node is right clicked.
14766 * @param {Roo.View} this
14767 * @param {Number} index The index of the target node
14768 * @param {HTMLElement} node The target node
14769 * @param {Roo.EventObject} e The raw event object
14771 "contextmenu" : true,
14773 * @event selectionchange
14774 * Fires when the selected nodes change.
14775 * @param {Roo.View} this
14776 * @param {Array} selections Array of the selected nodes
14778 "selectionchange" : true,
14781 * @event beforeselect
14782 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14783 * @param {Roo.View} this
14784 * @param {HTMLElement} node The node to be selected
14785 * @param {Array} selections Array of currently selected nodes
14787 "beforeselect" : true,
14789 * @event preparedata
14790 * Fires on every row to render, to allow you to change the data.
14791 * @param {Roo.View} this
14792 * @param {Object} data to be rendered (change this)
14794 "preparedata" : true
14802 "click": this.onClick,
14803 "dblclick": this.onDblClick,
14804 "contextmenu": this.onContextMenu,
14808 this.selections = [];
14810 this.cmp = new Roo.CompositeElementLite([]);
14812 this.store = Roo.factory(this.store, Roo.data);
14813 this.setStore(this.store, true);
14816 if ( this.footer && this.footer.xtype) {
14818 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14820 this.footer.dataSource = this.store;
14821 this.footer.container = fctr;
14822 this.footer = Roo.factory(this.footer, Roo);
14823 fctr.insertFirst(this.el);
14825 // this is a bit insane - as the paging toolbar seems to detach the el..
14826 // dom.parentNode.parentNode.parentNode
14827 // they get detached?
14831 Roo.View.superclass.constructor.call(this);
14836 Roo.extend(Roo.View, Roo.util.Observable, {
14839 * @cfg {Roo.data.Store} store Data store to load data from.
14844 * @cfg {String|Roo.Element} el The container element.
14849 * @cfg {String|Roo.Template} tpl The template used by this View
14853 * @cfg {String} dataName the named area of the template to use as the data area
14854 * Works with domtemplates roo-name="name"
14858 * @cfg {String} selectedClass The css class to add to selected nodes
14860 selectedClass : "x-view-selected",
14862 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14867 * @cfg {String} text to display on mask (default Loading)
14871 * @cfg {Boolean} multiSelect Allow multiple selection
14873 multiSelect : false,
14875 * @cfg {Boolean} singleSelect Allow single selection
14877 singleSelect: false,
14880 * @cfg {Boolean} toggleSelect - selecting
14882 toggleSelect : false,
14885 * @cfg {Boolean} tickable - selecting
14890 * Returns the element this view is bound to.
14891 * @return {Roo.Element}
14893 getEl : function(){
14894 return this.wrapEl;
14900 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14902 refresh : function(){
14903 //Roo.log('refresh');
14906 // if we are using something like 'domtemplate', then
14907 // the what gets used is:
14908 // t.applySubtemplate(NAME, data, wrapping data..)
14909 // the outer template then get' applied with
14910 // the store 'extra data'
14911 // and the body get's added to the
14912 // roo-name="data" node?
14913 // <span class='roo-tpl-{name}'></span> ?????
14917 this.clearSelections();
14918 this.el.update("");
14920 var records = this.store.getRange();
14921 if(records.length < 1) {
14923 // is this valid?? = should it render a template??
14925 this.el.update(this.emptyText);
14929 if (this.dataName) {
14930 this.el.update(t.apply(this.store.meta)); //????
14931 el = this.el.child('.roo-tpl-' + this.dataName);
14934 for(var i = 0, len = records.length; i < len; i++){
14935 var data = this.prepareData(records[i].data, i, records[i]);
14936 this.fireEvent("preparedata", this, data, i, records[i]);
14938 var d = Roo.apply({}, data);
14941 Roo.apply(d, {'roo-id' : Roo.id()});
14945 Roo.each(this.parent.item, function(item){
14946 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14949 Roo.apply(d, {'roo-data-checked' : 'checked'});
14953 html[html.length] = Roo.util.Format.trim(
14955 t.applySubtemplate(this.dataName, d, this.store.meta) :
14962 el.update(html.join(""));
14963 this.nodes = el.dom.childNodes;
14964 this.updateIndexes(0);
14969 * Function to override to reformat the data that is sent to
14970 * the template for each node.
14971 * DEPRICATED - use the preparedata event handler.
14972 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14973 * a JSON object for an UpdateManager bound view).
14975 prepareData : function(data, index, record)
14977 this.fireEvent("preparedata", this, data, index, record);
14981 onUpdate : function(ds, record){
14982 // Roo.log('on update');
14983 this.clearSelections();
14984 var index = this.store.indexOf(record);
14985 var n = this.nodes[index];
14986 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14987 n.parentNode.removeChild(n);
14988 this.updateIndexes(index, index);
14994 onAdd : function(ds, records, index)
14996 //Roo.log(['on Add', ds, records, index] );
14997 this.clearSelections();
14998 if(this.nodes.length == 0){
15002 var n = this.nodes[index];
15003 for(var i = 0, len = records.length; i < len; i++){
15004 var d = this.prepareData(records[i].data, i, records[i]);
15006 this.tpl.insertBefore(n, d);
15009 this.tpl.append(this.el, d);
15012 this.updateIndexes(index);
15015 onRemove : function(ds, record, index){
15016 // Roo.log('onRemove');
15017 this.clearSelections();
15018 var el = this.dataName ?
15019 this.el.child('.roo-tpl-' + this.dataName) :
15022 el.dom.removeChild(this.nodes[index]);
15023 this.updateIndexes(index);
15027 * Refresh an individual node.
15028 * @param {Number} index
15030 refreshNode : function(index){
15031 this.onUpdate(this.store, this.store.getAt(index));
15034 updateIndexes : function(startIndex, endIndex){
15035 var ns = this.nodes;
15036 startIndex = startIndex || 0;
15037 endIndex = endIndex || ns.length - 1;
15038 for(var i = startIndex; i <= endIndex; i++){
15039 ns[i].nodeIndex = i;
15044 * Changes the data store this view uses and refresh the view.
15045 * @param {Store} store
15047 setStore : function(store, initial){
15048 if(!initial && this.store){
15049 this.store.un("datachanged", this.refresh);
15050 this.store.un("add", this.onAdd);
15051 this.store.un("remove", this.onRemove);
15052 this.store.un("update", this.onUpdate);
15053 this.store.un("clear", this.refresh);
15054 this.store.un("beforeload", this.onBeforeLoad);
15055 this.store.un("load", this.onLoad);
15056 this.store.un("loadexception", this.onLoad);
15060 store.on("datachanged", this.refresh, this);
15061 store.on("add", this.onAdd, this);
15062 store.on("remove", this.onRemove, this);
15063 store.on("update", this.onUpdate, this);
15064 store.on("clear", this.refresh, this);
15065 store.on("beforeload", this.onBeforeLoad, this);
15066 store.on("load", this.onLoad, this);
15067 store.on("loadexception", this.onLoad, this);
15075 * onbeforeLoad - masks the loading area.
15078 onBeforeLoad : function(store,opts)
15080 //Roo.log('onBeforeLoad');
15082 this.el.update("");
15084 this.el.mask(this.mask ? this.mask : "Loading" );
15086 onLoad : function ()
15093 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15094 * @param {HTMLElement} node
15095 * @return {HTMLElement} The template node
15097 findItemFromChild : function(node){
15098 var el = this.dataName ?
15099 this.el.child('.roo-tpl-' + this.dataName,true) :
15102 if(!node || node.parentNode == el){
15105 var p = node.parentNode;
15106 while(p && p != el){
15107 if(p.parentNode == el){
15116 onClick : function(e){
15117 var item = this.findItemFromChild(e.getTarget());
15119 var index = this.indexOf(item);
15120 if(this.onItemClick(item, index, e) !== false){
15121 this.fireEvent("click", this, index, item, e);
15124 this.clearSelections();
15129 onContextMenu : function(e){
15130 var item = this.findItemFromChild(e.getTarget());
15132 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15137 onDblClick : function(e){
15138 var item = this.findItemFromChild(e.getTarget());
15140 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15144 onItemClick : function(item, index, e)
15146 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15149 if (this.toggleSelect) {
15150 var m = this.isSelected(item) ? 'unselect' : 'select';
15153 _t[m](item, true, false);
15156 if(this.multiSelect || this.singleSelect){
15157 if(this.multiSelect && e.shiftKey && this.lastSelection){
15158 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15160 this.select(item, this.multiSelect && e.ctrlKey);
15161 this.lastSelection = item;
15164 if(!this.tickable){
15165 e.preventDefault();
15173 * Get the number of selected nodes.
15176 getSelectionCount : function(){
15177 return this.selections.length;
15181 * Get the currently selected nodes.
15182 * @return {Array} An array of HTMLElements
15184 getSelectedNodes : function(){
15185 return this.selections;
15189 * Get the indexes of the selected nodes.
15192 getSelectedIndexes : function(){
15193 var indexes = [], s = this.selections;
15194 for(var i = 0, len = s.length; i < len; i++){
15195 indexes.push(s[i].nodeIndex);
15201 * Clear all selections
15202 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15204 clearSelections : function(suppressEvent){
15205 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15206 this.cmp.elements = this.selections;
15207 this.cmp.removeClass(this.selectedClass);
15208 this.selections = [];
15209 if(!suppressEvent){
15210 this.fireEvent("selectionchange", this, this.selections);
15216 * Returns true if the passed node is selected
15217 * @param {HTMLElement/Number} node The node or node index
15218 * @return {Boolean}
15220 isSelected : function(node){
15221 var s = this.selections;
15225 node = this.getNode(node);
15226 return s.indexOf(node) !== -1;
15231 * @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
15232 * @param {Boolean} keepExisting (optional) true to keep existing selections
15233 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15235 select : function(nodeInfo, keepExisting, suppressEvent){
15236 if(nodeInfo instanceof Array){
15238 this.clearSelections(true);
15240 for(var i = 0, len = nodeInfo.length; i < len; i++){
15241 this.select(nodeInfo[i], true, true);
15245 var node = this.getNode(nodeInfo);
15246 if(!node || this.isSelected(node)){
15247 return; // already selected.
15250 this.clearSelections(true);
15253 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15254 Roo.fly(node).addClass(this.selectedClass);
15255 this.selections.push(node);
15256 if(!suppressEvent){
15257 this.fireEvent("selectionchange", this, this.selections);
15265 * @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
15266 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15267 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15269 unselect : function(nodeInfo, keepExisting, suppressEvent)
15271 if(nodeInfo instanceof Array){
15272 Roo.each(this.selections, function(s) {
15273 this.unselect(s, nodeInfo);
15277 var node = this.getNode(nodeInfo);
15278 if(!node || !this.isSelected(node)){
15279 //Roo.log("not selected");
15280 return; // not selected.
15284 Roo.each(this.selections, function(s) {
15286 Roo.fly(node).removeClass(this.selectedClass);
15293 this.selections= ns;
15294 this.fireEvent("selectionchange", this, this.selections);
15298 * Gets a template node.
15299 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15300 * @return {HTMLElement} The node or null if it wasn't found
15302 getNode : function(nodeInfo){
15303 if(typeof nodeInfo == "string"){
15304 return document.getElementById(nodeInfo);
15305 }else if(typeof nodeInfo == "number"){
15306 return this.nodes[nodeInfo];
15312 * Gets a range template nodes.
15313 * @param {Number} startIndex
15314 * @param {Number} endIndex
15315 * @return {Array} An array of nodes
15317 getNodes : function(start, end){
15318 var ns = this.nodes;
15319 start = start || 0;
15320 end = typeof end == "undefined" ? ns.length - 1 : end;
15323 for(var i = start; i <= end; i++){
15327 for(var i = start; i >= end; i--){
15335 * Finds the index of the passed node
15336 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15337 * @return {Number} The index of the node or -1
15339 indexOf : function(node){
15340 node = this.getNode(node);
15341 if(typeof node.nodeIndex == "number"){
15342 return node.nodeIndex;
15344 var ns = this.nodes;
15345 for(var i = 0, len = ns.length; i < len; i++){
15356 * based on jquery fullcalendar
15360 Roo.bootstrap = Roo.bootstrap || {};
15362 * @class Roo.bootstrap.Calendar
15363 * @extends Roo.bootstrap.Component
15364 * Bootstrap Calendar class
15365 * @cfg {Boolean} loadMask (true|false) default false
15366 * @cfg {Object} header generate the user specific header of the calendar, default false
15369 * Create a new Container
15370 * @param {Object} config The config object
15375 Roo.bootstrap.Calendar = function(config){
15376 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15380 * Fires when a date is selected
15381 * @param {DatePicker} this
15382 * @param {Date} date The selected date
15386 * @event monthchange
15387 * Fires when the displayed month changes
15388 * @param {DatePicker} this
15389 * @param {Date} date The selected month
15391 'monthchange': true,
15393 * @event evententer
15394 * Fires when mouse over an event
15395 * @param {Calendar} this
15396 * @param {event} Event
15398 'evententer': true,
15400 * @event eventleave
15401 * Fires when the mouse leaves an
15402 * @param {Calendar} this
15405 'eventleave': true,
15407 * @event eventclick
15408 * Fires when the mouse click an
15409 * @param {Calendar} this
15418 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15421 * @cfg {Number} startDay
15422 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15430 getAutoCreate : function(){
15433 var fc_button = function(name, corner, style, content ) {
15434 return Roo.apply({},{
15436 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15438 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15441 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15452 style : 'width:100%',
15459 cls : 'fc-header-left',
15461 fc_button('prev', 'left', 'arrow', '‹' ),
15462 fc_button('next', 'right', 'arrow', '›' ),
15463 { tag: 'span', cls: 'fc-header-space' },
15464 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15472 cls : 'fc-header-center',
15476 cls: 'fc-header-title',
15479 html : 'month / year'
15487 cls : 'fc-header-right',
15489 /* fc_button('month', 'left', '', 'month' ),
15490 fc_button('week', '', '', 'week' ),
15491 fc_button('day', 'right', '', 'day' )
15503 header = this.header;
15506 var cal_heads = function() {
15508 // fixme - handle this.
15510 for (var i =0; i < Date.dayNames.length; i++) {
15511 var d = Date.dayNames[i];
15514 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15515 html : d.substring(0,3)
15519 ret[0].cls += ' fc-first';
15520 ret[6].cls += ' fc-last';
15523 var cal_cell = function(n) {
15526 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15531 cls: 'fc-day-number',
15535 cls: 'fc-day-content',
15539 style: 'position: relative;' // height: 17px;
15551 var cal_rows = function() {
15554 for (var r = 0; r < 6; r++) {
15561 for (var i =0; i < Date.dayNames.length; i++) {
15562 var d = Date.dayNames[i];
15563 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15566 row.cn[0].cls+=' fc-first';
15567 row.cn[0].cn[0].style = 'min-height:90px';
15568 row.cn[6].cls+=' fc-last';
15572 ret[0].cls += ' fc-first';
15573 ret[4].cls += ' fc-prev-last';
15574 ret[5].cls += ' fc-last';
15581 cls: 'fc-border-separate',
15582 style : 'width:100%',
15590 cls : 'fc-first fc-last',
15608 cls : 'fc-content',
15609 style : "position: relative;",
15612 cls : 'fc-view fc-view-month fc-grid',
15613 style : 'position: relative',
15614 unselectable : 'on',
15617 cls : 'fc-event-container',
15618 style : 'position:absolute;z-index:8;top:0;left:0;'
15636 initEvents : function()
15639 throw "can not find store for calendar";
15645 style: "text-align:center",
15649 style: "background-color:white;width:50%;margin:250 auto",
15653 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15664 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15666 var size = this.el.select('.fc-content', true).first().getSize();
15667 this.maskEl.setSize(size.width, size.height);
15668 this.maskEl.enableDisplayMode("block");
15669 if(!this.loadMask){
15670 this.maskEl.hide();
15673 this.store = Roo.factory(this.store, Roo.data);
15674 this.store.on('load', this.onLoad, this);
15675 this.store.on('beforeload', this.onBeforeLoad, this);
15679 this.cells = this.el.select('.fc-day',true);
15680 //Roo.log(this.cells);
15681 this.textNodes = this.el.query('.fc-day-number');
15682 this.cells.addClassOnOver('fc-state-hover');
15684 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15685 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15686 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15687 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15689 this.on('monthchange', this.onMonthChange, this);
15691 this.update(new Date().clearTime());
15694 resize : function() {
15695 var sz = this.el.getSize();
15697 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15698 this.el.select('.fc-day-content div',true).setHeight(34);
15703 showPrevMonth : function(e){
15704 this.update(this.activeDate.add("mo", -1));
15706 showToday : function(e){
15707 this.update(new Date().clearTime());
15710 showNextMonth : function(e){
15711 this.update(this.activeDate.add("mo", 1));
15715 showPrevYear : function(){
15716 this.update(this.activeDate.add("y", -1));
15720 showNextYear : function(){
15721 this.update(this.activeDate.add("y", 1));
15726 update : function(date)
15728 var vd = this.activeDate;
15729 this.activeDate = date;
15730 // if(vd && this.el){
15731 // var t = date.getTime();
15732 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15733 // Roo.log('using add remove');
15735 // this.fireEvent('monthchange', this, date);
15737 // this.cells.removeClass("fc-state-highlight");
15738 // this.cells.each(function(c){
15739 // if(c.dateValue == t){
15740 // c.addClass("fc-state-highlight");
15741 // setTimeout(function(){
15742 // try{c.dom.firstChild.focus();}catch(e){}
15752 var days = date.getDaysInMonth();
15754 var firstOfMonth = date.getFirstDateOfMonth();
15755 var startingPos = firstOfMonth.getDay()-this.startDay;
15757 if(startingPos < this.startDay){
15761 var pm = date.add(Date.MONTH, -1);
15762 var prevStart = pm.getDaysInMonth()-startingPos;
15764 this.cells = this.el.select('.fc-day',true);
15765 this.textNodes = this.el.query('.fc-day-number');
15766 this.cells.addClassOnOver('fc-state-hover');
15768 var cells = this.cells.elements;
15769 var textEls = this.textNodes;
15771 Roo.each(cells, function(cell){
15772 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15775 days += startingPos;
15777 // convert everything to numbers so it's fast
15778 var day = 86400000;
15779 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15782 //Roo.log(prevStart);
15784 var today = new Date().clearTime().getTime();
15785 var sel = date.clearTime().getTime();
15786 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15787 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15788 var ddMatch = this.disabledDatesRE;
15789 var ddText = this.disabledDatesText;
15790 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15791 var ddaysText = this.disabledDaysText;
15792 var format = this.format;
15794 var setCellClass = function(cal, cell){
15798 //Roo.log('set Cell Class');
15800 var t = d.getTime();
15804 cell.dateValue = t;
15806 cell.className += " fc-today";
15807 cell.className += " fc-state-highlight";
15808 cell.title = cal.todayText;
15811 // disable highlight in other month..
15812 //cell.className += " fc-state-highlight";
15817 cell.className = " fc-state-disabled";
15818 cell.title = cal.minText;
15822 cell.className = " fc-state-disabled";
15823 cell.title = cal.maxText;
15827 if(ddays.indexOf(d.getDay()) != -1){
15828 cell.title = ddaysText;
15829 cell.className = " fc-state-disabled";
15832 if(ddMatch && format){
15833 var fvalue = d.dateFormat(format);
15834 if(ddMatch.test(fvalue)){
15835 cell.title = ddText.replace("%0", fvalue);
15836 cell.className = " fc-state-disabled";
15840 if (!cell.initialClassName) {
15841 cell.initialClassName = cell.dom.className;
15844 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15849 for(; i < startingPos; i++) {
15850 textEls[i].innerHTML = (++prevStart);
15851 d.setDate(d.getDate()+1);
15853 cells[i].className = "fc-past fc-other-month";
15854 setCellClass(this, cells[i]);
15859 for(; i < days; i++){
15860 intDay = i - startingPos + 1;
15861 textEls[i].innerHTML = (intDay);
15862 d.setDate(d.getDate()+1);
15864 cells[i].className = ''; // "x-date-active";
15865 setCellClass(this, cells[i]);
15869 for(; i < 42; i++) {
15870 textEls[i].innerHTML = (++extraDays);
15871 d.setDate(d.getDate()+1);
15873 cells[i].className = "fc-future fc-other-month";
15874 setCellClass(this, cells[i]);
15877 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15879 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15881 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15882 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15884 if(totalRows != 6){
15885 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15886 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15889 this.fireEvent('monthchange', this, date);
15893 if(!this.internalRender){
15894 var main = this.el.dom.firstChild;
15895 var w = main.offsetWidth;
15896 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15897 Roo.fly(main).setWidth(w);
15898 this.internalRender = true;
15899 // opera does not respect the auto grow header center column
15900 // then, after it gets a width opera refuses to recalculate
15901 // without a second pass
15902 if(Roo.isOpera && !this.secondPass){
15903 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15904 this.secondPass = true;
15905 this.update.defer(10, this, [date]);
15912 findCell : function(dt) {
15913 dt = dt.clearTime().getTime();
15915 this.cells.each(function(c){
15916 //Roo.log("check " +c.dateValue + '?=' + dt);
15917 if(c.dateValue == dt){
15927 findCells : function(ev) {
15928 var s = ev.start.clone().clearTime().getTime();
15930 var e= ev.end.clone().clearTime().getTime();
15933 this.cells.each(function(c){
15934 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15936 if(c.dateValue > e){
15939 if(c.dateValue < s){
15948 // findBestRow: function(cells)
15952 // for (var i =0 ; i < cells.length;i++) {
15953 // ret = Math.max(cells[i].rows || 0,ret);
15960 addItem : function(ev)
15962 // look for vertical location slot in
15963 var cells = this.findCells(ev);
15965 // ev.row = this.findBestRow(cells);
15967 // work out the location.
15971 for(var i =0; i < cells.length; i++) {
15973 cells[i].row = cells[0].row;
15976 cells[i].row = cells[i].row + 1;
15986 if (crow.start.getY() == cells[i].getY()) {
15988 crow.end = cells[i];
16005 cells[0].events.push(ev);
16007 this.calevents.push(ev);
16010 clearEvents: function() {
16012 if(!this.calevents){
16016 Roo.each(this.cells.elements, function(c){
16022 Roo.each(this.calevents, function(e) {
16023 Roo.each(e.els, function(el) {
16024 el.un('mouseenter' ,this.onEventEnter, this);
16025 el.un('mouseleave' ,this.onEventLeave, this);
16030 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16036 renderEvents: function()
16040 this.cells.each(function(c) {
16049 if(c.row != c.events.length){
16050 r = 4 - (4 - (c.row - c.events.length));
16053 c.events = ev.slice(0, r);
16054 c.more = ev.slice(r);
16056 if(c.more.length && c.more.length == 1){
16057 c.events.push(c.more.pop());
16060 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16064 this.cells.each(function(c) {
16066 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16069 for (var e = 0; e < c.events.length; e++){
16070 var ev = c.events[e];
16071 var rows = ev.rows;
16073 for(var i = 0; i < rows.length; i++) {
16075 // how many rows should it span..
16078 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16079 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16081 unselectable : "on",
16084 cls: 'fc-event-inner',
16088 // cls: 'fc-event-time',
16089 // html : cells.length > 1 ? '' : ev.time
16093 cls: 'fc-event-title',
16094 html : String.format('{0}', ev.title)
16101 cls: 'ui-resizable-handle ui-resizable-e',
16102 html : '  '
16109 cfg.cls += ' fc-event-start';
16111 if ((i+1) == rows.length) {
16112 cfg.cls += ' fc-event-end';
16115 var ctr = _this.el.select('.fc-event-container',true).first();
16116 var cg = ctr.createChild(cfg);
16118 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16119 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16121 var r = (c.more.length) ? 1 : 0;
16122 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16123 cg.setWidth(ebox.right - sbox.x -2);
16125 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16126 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16127 cg.on('click', _this.onEventClick, _this, ev);
16138 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16139 style : 'position: absolute',
16140 unselectable : "on",
16143 cls: 'fc-event-inner',
16147 cls: 'fc-event-title',
16155 cls: 'ui-resizable-handle ui-resizable-e',
16156 html : '  '
16162 var ctr = _this.el.select('.fc-event-container',true).first();
16163 var cg = ctr.createChild(cfg);
16165 var sbox = c.select('.fc-day-content',true).first().getBox();
16166 var ebox = c.select('.fc-day-content',true).first().getBox();
16168 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16169 cg.setWidth(ebox.right - sbox.x -2);
16171 cg.on('click', _this.onMoreEventClick, _this, c.more);
16181 onEventEnter: function (e, el,event,d) {
16182 this.fireEvent('evententer', this, el, event);
16185 onEventLeave: function (e, el,event,d) {
16186 this.fireEvent('eventleave', this, el, event);
16189 onEventClick: function (e, el,event,d) {
16190 this.fireEvent('eventclick', this, el, event);
16193 onMonthChange: function () {
16197 onMoreEventClick: function(e, el, more)
16201 this.calpopover.placement = 'right';
16202 this.calpopover.setTitle('More');
16204 this.calpopover.setContent('');
16206 var ctr = this.calpopover.el.select('.popover-content', true).first();
16208 Roo.each(more, function(m){
16210 cls : 'fc-event-hori fc-event-draggable',
16213 var cg = ctr.createChild(cfg);
16215 cg.on('click', _this.onEventClick, _this, m);
16218 this.calpopover.show(el);
16223 onLoad: function ()
16225 this.calevents = [];
16228 if(this.store.getCount() > 0){
16229 this.store.data.each(function(d){
16232 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16233 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16234 time : d.data.start_time,
16235 title : d.data.title,
16236 description : d.data.description,
16237 venue : d.data.venue
16242 this.renderEvents();
16244 if(this.calevents.length && this.loadMask){
16245 this.maskEl.hide();
16249 onBeforeLoad: function()
16251 this.clearEvents();
16253 this.maskEl.show();
16267 * @class Roo.bootstrap.Popover
16268 * @extends Roo.bootstrap.Component
16269 * Bootstrap Popover class
16270 * @cfg {String} html contents of the popover (or false to use children..)
16271 * @cfg {String} title of popover (or false to hide)
16272 * @cfg {String} placement how it is placed
16273 * @cfg {String} trigger click || hover (or false to trigger manually)
16274 * @cfg {String} over what (parent or false to trigger manually.)
16275 * @cfg {Number} delay - delay before showing
16278 * Create a new Popover
16279 * @param {Object} config The config object
16282 Roo.bootstrap.Popover = function(config){
16283 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16289 * After the popover show
16291 * @param {Roo.bootstrap.Popover} this
16296 * After the popover hide
16298 * @param {Roo.bootstrap.Popover} this
16304 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16306 title: 'Fill in a title',
16309 placement : 'right',
16310 trigger : 'hover', // hover
16316 can_build_overlaid : false,
16318 getChildContainer : function()
16320 return this.el.select('.popover-content',true).first();
16323 getAutoCreate : function(){
16326 cls : 'popover roo-dynamic',
16327 style: 'display:block',
16333 cls : 'popover-inner',
16337 cls: 'popover-title',
16341 cls : 'popover-content',
16352 setTitle: function(str)
16355 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16357 setContent: function(str)
16360 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16362 // as it get's added to the bottom of the page.
16363 onRender : function(ct, position)
16365 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16367 var cfg = Roo.apply({}, this.getAutoCreate());
16371 cfg.cls += ' ' + this.cls;
16374 cfg.style = this.style;
16376 //Roo.log("adding to ");
16377 this.el = Roo.get(document.body).createChild(cfg, position);
16378 // Roo.log(this.el);
16383 initEvents : function()
16385 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16386 this.el.enableDisplayMode('block');
16388 if (this.over === false) {
16391 if (this.triggers === false) {
16394 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16395 var triggers = this.trigger ? this.trigger.split(' ') : [];
16396 Roo.each(triggers, function(trigger) {
16398 if (trigger == 'click') {
16399 on_el.on('click', this.toggle, this);
16400 } else if (trigger != 'manual') {
16401 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16402 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16404 on_el.on(eventIn ,this.enter, this);
16405 on_el.on(eventOut, this.leave, this);
16416 toggle : function () {
16417 this.hoverState == 'in' ? this.leave() : this.enter();
16420 enter : function () {
16422 clearTimeout(this.timeout);
16424 this.hoverState = 'in';
16426 if (!this.delay || !this.delay.show) {
16431 this.timeout = setTimeout(function () {
16432 if (_t.hoverState == 'in') {
16435 }, this.delay.show)
16438 leave : function() {
16439 clearTimeout(this.timeout);
16441 this.hoverState = 'out';
16443 if (!this.delay || !this.delay.hide) {
16448 this.timeout = setTimeout(function () {
16449 if (_t.hoverState == 'out') {
16452 }, this.delay.hide)
16455 show : function (on_el)
16458 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16462 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16463 if (this.html !== false) {
16464 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16466 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16467 if (!this.title.length) {
16468 this.el.select('.popover-title',true).hide();
16471 var placement = typeof this.placement == 'function' ?
16472 this.placement.call(this, this.el, on_el) :
16475 var autoToken = /\s?auto?\s?/i;
16476 var autoPlace = autoToken.test(placement);
16478 placement = placement.replace(autoToken, '') || 'top';
16482 //this.el.setXY([0,0]);
16484 this.el.dom.style.display='block';
16485 this.el.addClass(placement);
16487 //this.el.appendTo(on_el);
16489 var p = this.getPosition();
16490 var box = this.el.getBox();
16495 var align = Roo.bootstrap.Popover.alignment[placement];
16496 this.el.alignTo(on_el, align[0],align[1]);
16497 //var arrow = this.el.select('.arrow',true).first();
16498 //arrow.set(align[2],
16500 this.el.addClass('in');
16503 if (this.el.hasClass('fade')) {
16507 this.hoverState = 'in';
16509 this.fireEvent('show', this);
16514 this.el.setXY([0,0]);
16515 this.el.removeClass('in');
16517 this.hoverState = null;
16519 this.fireEvent('hide', this);
16524 Roo.bootstrap.Popover.alignment = {
16525 'left' : ['r-l', [-10,0], 'right'],
16526 'right' : ['l-r', [10,0], 'left'],
16527 'bottom' : ['t-b', [0,10], 'top'],
16528 'top' : [ 'b-t', [0,-10], 'bottom']
16539 * @class Roo.bootstrap.Progress
16540 * @extends Roo.bootstrap.Component
16541 * Bootstrap Progress class
16542 * @cfg {Boolean} striped striped of the progress bar
16543 * @cfg {Boolean} active animated of the progress bar
16547 * Create a new Progress
16548 * @param {Object} config The config object
16551 Roo.bootstrap.Progress = function(config){
16552 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16555 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16560 getAutoCreate : function(){
16568 cfg.cls += ' progress-striped';
16572 cfg.cls += ' active';
16591 * @class Roo.bootstrap.ProgressBar
16592 * @extends Roo.bootstrap.Component
16593 * Bootstrap ProgressBar class
16594 * @cfg {Number} aria_valuenow aria-value now
16595 * @cfg {Number} aria_valuemin aria-value min
16596 * @cfg {Number} aria_valuemax aria-value max
16597 * @cfg {String} label label for the progress bar
16598 * @cfg {String} panel (success | info | warning | danger )
16599 * @cfg {String} role role of the progress bar
16600 * @cfg {String} sr_only text
16604 * Create a new ProgressBar
16605 * @param {Object} config The config object
16608 Roo.bootstrap.ProgressBar = function(config){
16609 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16612 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16616 aria_valuemax : 100,
16622 getAutoCreate : function()
16627 cls: 'progress-bar',
16628 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16640 cfg.role = this.role;
16643 if(this.aria_valuenow){
16644 cfg['aria-valuenow'] = this.aria_valuenow;
16647 if(this.aria_valuemin){
16648 cfg['aria-valuemin'] = this.aria_valuemin;
16651 if(this.aria_valuemax){
16652 cfg['aria-valuemax'] = this.aria_valuemax;
16655 if(this.label && !this.sr_only){
16656 cfg.html = this.label;
16660 cfg.cls += ' progress-bar-' + this.panel;
16666 update : function(aria_valuenow)
16668 this.aria_valuenow = aria_valuenow;
16670 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16685 * @class Roo.bootstrap.TabGroup
16686 * @extends Roo.bootstrap.Column
16687 * Bootstrap Column class
16688 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16689 * @cfg {Boolean} carousel true to make the group behave like a carousel
16690 * @cfg {Boolean} bullets show bullets for the panels
16691 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16692 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16693 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16694 * @cfg {Boolean} showarrow (true|false) show arrow default true
16697 * Create a new TabGroup
16698 * @param {Object} config The config object
16701 Roo.bootstrap.TabGroup = function(config){
16702 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16704 this.navId = Roo.id();
16707 Roo.bootstrap.TabGroup.register(this);
16711 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16714 transition : false,
16719 slideOnTouch : false,
16722 getAutoCreate : function()
16724 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16726 cfg.cls += ' tab-content';
16728 if (this.carousel) {
16729 cfg.cls += ' carousel slide';
16732 cls : 'carousel-inner',
16736 if(this.bullets && !Roo.isTouch){
16739 cls : 'carousel-bullets',
16743 if(this.bullets_cls){
16744 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16751 cfg.cn[0].cn.push(bullets);
16754 if(this.showarrow){
16755 cfg.cn[0].cn.push({
16757 class : 'carousel-arrow',
16761 class : 'carousel-prev',
16765 class : 'fa fa-chevron-left'
16771 class : 'carousel-next',
16775 class : 'fa fa-chevron-right'
16788 initEvents: function()
16790 if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16791 this.el.on("touchstart", this.onTouchStart, this);
16794 if(this.autoslide){
16797 this.slideFn = window.setInterval(function() {
16798 _this.showPanelNext();
16802 if(this.showarrow){
16803 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16804 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16810 onTouchStart : function(e, el, o)
16812 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16816 this.showPanelNext();
16819 getChildContainer : function()
16821 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16825 * register a Navigation item
16826 * @param {Roo.bootstrap.NavItem} the navitem to add
16828 register : function(item)
16830 this.tabs.push( item);
16831 item.navId = this.navId; // not really needed..
16836 getActivePanel : function()
16839 Roo.each(this.tabs, function(t) {
16849 getPanelByName : function(n)
16852 Roo.each(this.tabs, function(t) {
16853 if (t.tabId == n) {
16861 indexOfPanel : function(p)
16864 Roo.each(this.tabs, function(t,i) {
16865 if (t.tabId == p.tabId) {
16874 * show a specific panel
16875 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16876 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16878 showPanel : function (pan)
16880 if(this.transition || typeof(pan) == 'undefined'){
16881 Roo.log("waiting for the transitionend");
16885 if (typeof(pan) == 'number') {
16886 pan = this.tabs[pan];
16889 if (typeof(pan) == 'string') {
16890 pan = this.getPanelByName(pan);
16893 var cur = this.getActivePanel();
16896 Roo.log('pan or acitve pan is undefined');
16900 if (pan.tabId == this.getActivePanel().tabId) {
16904 if (false === cur.fireEvent('beforedeactivate')) {
16908 if(this.bullets > 0 && !Roo.isTouch){
16909 this.setActiveBullet(this.indexOfPanel(pan));
16912 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16914 this.transition = true;
16915 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16916 var lr = dir == 'next' ? 'left' : 'right';
16917 pan.el.addClass(dir); // or prev
16918 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16919 cur.el.addClass(lr); // or right
16920 pan.el.addClass(lr);
16923 cur.el.on('transitionend', function() {
16924 Roo.log("trans end?");
16926 pan.el.removeClass([lr,dir]);
16927 pan.setActive(true);
16929 cur.el.removeClass([lr]);
16930 cur.setActive(false);
16932 _this.transition = false;
16934 }, this, { single: true } );
16939 cur.setActive(false);
16940 pan.setActive(true);
16945 showPanelNext : function()
16947 var i = this.indexOfPanel(this.getActivePanel());
16949 if (i >= this.tabs.length - 1 && !this.autoslide) {
16953 if (i >= this.tabs.length - 1 && this.autoslide) {
16957 this.showPanel(this.tabs[i+1]);
16960 showPanelPrev : function()
16962 var i = this.indexOfPanel(this.getActivePanel());
16964 if (i < 1 && !this.autoslide) {
16968 if (i < 1 && this.autoslide) {
16969 i = this.tabs.length;
16972 this.showPanel(this.tabs[i-1]);
16976 addBullet: function()
16978 if(!this.bullets || Roo.isTouch){
16981 var ctr = this.el.select('.carousel-bullets',true).first();
16982 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16983 var bullet = ctr.createChild({
16984 cls : 'bullet bullet-' + i
16985 },ctr.dom.lastChild);
16990 bullet.on('click', (function(e, el, o, ii, t){
16992 e.preventDefault();
16994 this.showPanel(ii);
16996 if(this.autoslide && this.slideFn){
16997 clearInterval(this.slideFn);
16998 this.slideFn = window.setInterval(function() {
16999 _this.showPanelNext();
17003 }).createDelegate(this, [i, bullet], true));
17008 setActiveBullet : function(i)
17014 Roo.each(this.el.select('.bullet', true).elements, function(el){
17015 el.removeClass('selected');
17018 var bullet = this.el.select('.bullet-' + i, true).first();
17024 bullet.addClass('selected');
17035 Roo.apply(Roo.bootstrap.TabGroup, {
17039 * register a Navigation Group
17040 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17042 register : function(navgrp)
17044 this.groups[navgrp.navId] = navgrp;
17048 * fetch a Navigation Group based on the navigation ID
17049 * if one does not exist , it will get created.
17050 * @param {string} the navgroup to add
17051 * @returns {Roo.bootstrap.NavGroup} the navgroup
17053 get: function(navId) {
17054 if (typeof(this.groups[navId]) == 'undefined') {
17055 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17057 return this.groups[navId] ;
17072 * @class Roo.bootstrap.TabPanel
17073 * @extends Roo.bootstrap.Component
17074 * Bootstrap TabPanel class
17075 * @cfg {Boolean} active panel active
17076 * @cfg {String} html panel content
17077 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17078 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17079 * @cfg {String} href click to link..
17083 * Create a new TabPanel
17084 * @param {Object} config The config object
17087 Roo.bootstrap.TabPanel = function(config){
17088 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17092 * Fires when the active status changes
17093 * @param {Roo.bootstrap.TabPanel} this
17094 * @param {Boolean} state the new state
17099 * @event beforedeactivate
17100 * Fires before a tab is de-activated - can be used to do validation on a form.
17101 * @param {Roo.bootstrap.TabPanel} this
17102 * @return {Boolean} false if there is an error
17105 'beforedeactivate': true
17108 this.tabId = this.tabId || Roo.id();
17112 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17120 getAutoCreate : function(){
17123 // item is needed for carousel - not sure if it has any effect otherwise
17124 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17125 html: this.html || ''
17129 cfg.cls += ' active';
17133 cfg.tabId = this.tabId;
17140 initEvents: function()
17142 var p = this.parent();
17143 this.navId = this.navId || p.navId;
17145 if (typeof(this.navId) != 'undefined') {
17146 // not really needed.. but just in case.. parent should be a NavGroup.
17147 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17151 var i = tg.tabs.length - 1;
17153 if(this.active && tg.bullets > 0 && i < tg.bullets){
17154 tg.setActiveBullet(i);
17158 if(this.href.length){
17159 this.el.on('click', this.onClick, this);
17164 onRender : function(ct, position)
17166 // Roo.log("Call onRender: " + this.xtype);
17168 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17176 setActive: function(state)
17178 Roo.log("panel - set active " + this.tabId + "=" + state);
17180 this.active = state;
17182 this.el.removeClass('active');
17184 } else if (!this.el.hasClass('active')) {
17185 this.el.addClass('active');
17188 this.fireEvent('changed', this, state);
17191 onClick: function(e)
17193 e.preventDefault();
17195 window.location.href = this.href;
17212 * @class Roo.bootstrap.DateField
17213 * @extends Roo.bootstrap.Input
17214 * Bootstrap DateField class
17215 * @cfg {Number} weekStart default 0
17216 * @cfg {String} viewMode default empty, (months|years)
17217 * @cfg {String} minViewMode default empty, (months|years)
17218 * @cfg {Number} startDate default -Infinity
17219 * @cfg {Number} endDate default Infinity
17220 * @cfg {Boolean} todayHighlight default false
17221 * @cfg {Boolean} todayBtn default false
17222 * @cfg {Boolean} calendarWeeks default false
17223 * @cfg {Object} daysOfWeekDisabled default empty
17224 * @cfg {Boolean} singleMode default false (true | false)
17226 * @cfg {Boolean} keyboardNavigation default true
17227 * @cfg {String} language default en
17230 * Create a new DateField
17231 * @param {Object} config The config object
17234 Roo.bootstrap.DateField = function(config){
17235 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17239 * Fires when this field show.
17240 * @param {Roo.bootstrap.DateField} this
17241 * @param {Mixed} date The date value
17246 * Fires when this field hide.
17247 * @param {Roo.bootstrap.DateField} this
17248 * @param {Mixed} date The date value
17253 * Fires when select a date.
17254 * @param {Roo.bootstrap.DateField} this
17255 * @param {Mixed} date The date value
17259 * @event beforeselect
17260 * Fires when before select a date.
17261 * @param {Roo.bootstrap.DateField} this
17262 * @param {Mixed} date The date value
17264 beforeselect : true
17268 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
17271 * @cfg {String} format
17272 * The default date format string which can be overriden for localization support. The format must be
17273 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17277 * @cfg {String} altFormats
17278 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17279 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17281 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17289 todayHighlight : false,
17295 keyboardNavigation: true,
17297 calendarWeeks: false,
17299 startDate: -Infinity,
17303 daysOfWeekDisabled: [],
17307 singleMode : false,
17309 UTCDate: function()
17311 return new Date(Date.UTC.apply(Date, arguments));
17314 UTCToday: function()
17316 var today = new Date();
17317 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17320 getDate: function() {
17321 var d = this.getUTCDate();
17322 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17325 getUTCDate: function() {
17329 setDate: function(d) {
17330 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17333 setUTCDate: function(d) {
17335 this.setValue(this.formatDate(this.date));
17338 onRender: function(ct, position)
17341 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17343 this.language = this.language || 'en';
17344 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17345 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17347 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17348 this.format = this.format || 'm/d/y';
17349 this.isInline = false;
17350 this.isInput = true;
17351 this.component = this.el.select('.add-on', true).first() || false;
17352 this.component = (this.component && this.component.length === 0) ? false : this.component;
17353 this.hasInput = this.component && this.inputEl().length;
17355 if (typeof(this.minViewMode === 'string')) {
17356 switch (this.minViewMode) {
17358 this.minViewMode = 1;
17361 this.minViewMode = 2;
17364 this.minViewMode = 0;
17369 if (typeof(this.viewMode === 'string')) {
17370 switch (this.viewMode) {
17383 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17385 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17387 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17389 this.picker().on('mousedown', this.onMousedown, this);
17390 this.picker().on('click', this.onClick, this);
17392 this.picker().addClass('datepicker-dropdown');
17394 this.startViewMode = this.viewMode;
17396 if(this.singleMode){
17397 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17398 v.setVisibilityMode(Roo.Element.DISPLAY);
17402 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17403 v.setStyle('width', '189px');
17407 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17408 if(!this.calendarWeeks){
17413 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17414 v.attr('colspan', function(i, val){
17415 return parseInt(val) + 1;
17420 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17422 this.setStartDate(this.startDate);
17423 this.setEndDate(this.endDate);
17425 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17432 if(this.isInline) {
17437 picker : function()
17439 return this.pickerEl;
17440 // return this.el.select('.datepicker', true).first();
17443 fillDow: function()
17445 var dowCnt = this.weekStart;
17454 if(this.calendarWeeks){
17462 while (dowCnt < this.weekStart + 7) {
17466 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17470 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17473 fillMonths: function()
17476 var months = this.picker().select('>.datepicker-months td', true).first();
17478 months.dom.innerHTML = '';
17484 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17487 months.createChild(month);
17494 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;
17496 if (this.date < this.startDate) {
17497 this.viewDate = new Date(this.startDate);
17498 } else if (this.date > this.endDate) {
17499 this.viewDate = new Date(this.endDate);
17501 this.viewDate = new Date(this.date);
17509 var d = new Date(this.viewDate),
17510 year = d.getUTCFullYear(),
17511 month = d.getUTCMonth(),
17512 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17513 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17514 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17515 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17516 currentDate = this.date && this.date.valueOf(),
17517 today = this.UTCToday();
17519 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17521 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17523 // this.picker.select('>tfoot th.today').
17524 // .text(dates[this.language].today)
17525 // .toggle(this.todayBtn !== false);
17527 this.updateNavArrows();
17530 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17532 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17534 prevMonth.setUTCDate(day);
17536 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17538 var nextMonth = new Date(prevMonth);
17540 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17542 nextMonth = nextMonth.valueOf();
17544 var fillMonths = false;
17546 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17548 while(prevMonth.valueOf() < nextMonth) {
17551 if (prevMonth.getUTCDay() === this.weekStart) {
17553 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17561 if(this.calendarWeeks){
17562 // ISO 8601: First week contains first thursday.
17563 // ISO also states week starts on Monday, but we can be more abstract here.
17565 // Start of current week: based on weekstart/current date
17566 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17567 // Thursday of this week
17568 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17569 // First Thursday of year, year from thursday
17570 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17571 // Calendar week: ms between thursdays, div ms per day, div 7 days
17572 calWeek = (th - yth) / 864e5 / 7 + 1;
17574 fillMonths.cn.push({
17582 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17584 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17587 if (this.todayHighlight &&
17588 prevMonth.getUTCFullYear() == today.getFullYear() &&
17589 prevMonth.getUTCMonth() == today.getMonth() &&
17590 prevMonth.getUTCDate() == today.getDate()) {
17591 clsName += ' today';
17594 if (currentDate && prevMonth.valueOf() === currentDate) {
17595 clsName += ' active';
17598 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17599 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17600 clsName += ' disabled';
17603 fillMonths.cn.push({
17605 cls: 'day ' + clsName,
17606 html: prevMonth.getDate()
17609 prevMonth.setDate(prevMonth.getDate()+1);
17612 var currentYear = this.date && this.date.getUTCFullYear();
17613 var currentMonth = this.date && this.date.getUTCMonth();
17615 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17617 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17618 v.removeClass('active');
17620 if(currentYear === year && k === currentMonth){
17621 v.addClass('active');
17624 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17625 v.addClass('disabled');
17631 year = parseInt(year/10, 10) * 10;
17633 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17635 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17638 for (var i = -1; i < 11; i++) {
17639 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17641 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17649 showMode: function(dir)
17652 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17655 Roo.each(this.picker().select('>div',true).elements, function(v){
17656 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17659 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17664 if(this.isInline) {
17668 this.picker().removeClass(['bottom', 'top']);
17670 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17672 * place to the top of element!
17676 this.picker().addClass('top');
17677 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17682 this.picker().addClass('bottom');
17684 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17687 parseDate : function(value)
17689 if(!value || value instanceof Date){
17692 var v = Date.parseDate(value, this.format);
17693 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17694 v = Date.parseDate(value, 'Y-m-d');
17696 if(!v && this.altFormats){
17697 if(!this.altFormatsArray){
17698 this.altFormatsArray = this.altFormats.split("|");
17700 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17701 v = Date.parseDate(value, this.altFormatsArray[i]);
17707 formatDate : function(date, fmt)
17709 return (!date || !(date instanceof Date)) ?
17710 date : date.dateFormat(fmt || this.format);
17713 onFocus : function()
17715 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17719 onBlur : function()
17721 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17723 var d = this.inputEl().getValue();
17732 this.picker().show();
17736 this.fireEvent('show', this, this.date);
17741 if(this.isInline) {
17744 this.picker().hide();
17745 this.viewMode = this.startViewMode;
17748 this.fireEvent('hide', this, this.date);
17752 onMousedown: function(e)
17754 e.stopPropagation();
17755 e.preventDefault();
17760 Roo.bootstrap.DateField.superclass.keyup.call(this);
17764 setValue: function(v)
17766 if(this.fireEvent('beforeselect', this, v) !== false){
17767 var d = new Date(this.parseDate(v) ).clearTime();
17769 if(isNaN(d.getTime())){
17770 this.date = this.viewDate = '';
17771 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17775 v = this.formatDate(d);
17777 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17779 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17783 this.fireEvent('select', this, this.date);
17787 getValue: function()
17789 return this.formatDate(this.date);
17792 fireKey: function(e)
17794 if (!this.picker().isVisible()){
17795 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17801 var dateChanged = false,
17803 newDate, newViewDate;
17808 e.preventDefault();
17812 if (!this.keyboardNavigation) {
17815 dir = e.keyCode == 37 ? -1 : 1;
17818 newDate = this.moveYear(this.date, dir);
17819 newViewDate = this.moveYear(this.viewDate, dir);
17820 } else if (e.shiftKey){
17821 newDate = this.moveMonth(this.date, dir);
17822 newViewDate = this.moveMonth(this.viewDate, dir);
17824 newDate = new Date(this.date);
17825 newDate.setUTCDate(this.date.getUTCDate() + dir);
17826 newViewDate = new Date(this.viewDate);
17827 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17829 if (this.dateWithinRange(newDate)){
17830 this.date = newDate;
17831 this.viewDate = newViewDate;
17832 this.setValue(this.formatDate(this.date));
17834 e.preventDefault();
17835 dateChanged = true;
17840 if (!this.keyboardNavigation) {
17843 dir = e.keyCode == 38 ? -1 : 1;
17845 newDate = this.moveYear(this.date, dir);
17846 newViewDate = this.moveYear(this.viewDate, dir);
17847 } else if (e.shiftKey){
17848 newDate = this.moveMonth(this.date, dir);
17849 newViewDate = this.moveMonth(this.viewDate, dir);
17851 newDate = new Date(this.date);
17852 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17853 newViewDate = new Date(this.viewDate);
17854 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17856 if (this.dateWithinRange(newDate)){
17857 this.date = newDate;
17858 this.viewDate = newViewDate;
17859 this.setValue(this.formatDate(this.date));
17861 e.preventDefault();
17862 dateChanged = true;
17866 this.setValue(this.formatDate(this.date));
17868 e.preventDefault();
17871 this.setValue(this.formatDate(this.date));
17885 onClick: function(e)
17887 e.stopPropagation();
17888 e.preventDefault();
17890 var target = e.getTarget();
17892 if(target.nodeName.toLowerCase() === 'i'){
17893 target = Roo.get(target).dom.parentNode;
17896 var nodeName = target.nodeName;
17897 var className = target.className;
17898 var html = target.innerHTML;
17899 //Roo.log(nodeName);
17901 switch(nodeName.toLowerCase()) {
17903 switch(className) {
17909 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17910 switch(this.viewMode){
17912 this.viewDate = this.moveMonth(this.viewDate, dir);
17916 this.viewDate = this.moveYear(this.viewDate, dir);
17922 var date = new Date();
17923 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17925 this.setValue(this.formatDate(this.date));
17932 if (className.indexOf('disabled') < 0) {
17933 this.viewDate.setUTCDate(1);
17934 if (className.indexOf('month') > -1) {
17935 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17937 var year = parseInt(html, 10) || 0;
17938 this.viewDate.setUTCFullYear(year);
17942 if(this.singleMode){
17943 this.setValue(this.formatDate(this.viewDate));
17954 //Roo.log(className);
17955 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17956 var day = parseInt(html, 10) || 1;
17957 var year = this.viewDate.getUTCFullYear(),
17958 month = this.viewDate.getUTCMonth();
17960 if (className.indexOf('old') > -1) {
17967 } else if (className.indexOf('new') > -1) {
17975 //Roo.log([year,month,day]);
17976 this.date = this.UTCDate(year, month, day,0,0,0,0);
17977 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17979 //Roo.log(this.formatDate(this.date));
17980 this.setValue(this.formatDate(this.date));
17987 setStartDate: function(startDate)
17989 this.startDate = startDate || -Infinity;
17990 if (this.startDate !== -Infinity) {
17991 this.startDate = this.parseDate(this.startDate);
17994 this.updateNavArrows();
17997 setEndDate: function(endDate)
17999 this.endDate = endDate || Infinity;
18000 if (this.endDate !== Infinity) {
18001 this.endDate = this.parseDate(this.endDate);
18004 this.updateNavArrows();
18007 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18009 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18010 if (typeof(this.daysOfWeekDisabled) !== 'object') {
18011 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18013 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18014 return parseInt(d, 10);
18017 this.updateNavArrows();
18020 updateNavArrows: function()
18022 if(this.singleMode){
18026 var d = new Date(this.viewDate),
18027 year = d.getUTCFullYear(),
18028 month = d.getUTCMonth();
18030 Roo.each(this.picker().select('.prev', true).elements, function(v){
18032 switch (this.viewMode) {
18035 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18041 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18048 Roo.each(this.picker().select('.next', true).elements, function(v){
18050 switch (this.viewMode) {
18053 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18059 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18067 moveMonth: function(date, dir)
18072 var new_date = new Date(date.valueOf()),
18073 day = new_date.getUTCDate(),
18074 month = new_date.getUTCMonth(),
18075 mag = Math.abs(dir),
18077 dir = dir > 0 ? 1 : -1;
18080 // If going back one month, make sure month is not current month
18081 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18083 return new_date.getUTCMonth() == month;
18085 // If going forward one month, make sure month is as expected
18086 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18088 return new_date.getUTCMonth() != new_month;
18090 new_month = month + dir;
18091 new_date.setUTCMonth(new_month);
18092 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18093 if (new_month < 0 || new_month > 11) {
18094 new_month = (new_month + 12) % 12;
18097 // For magnitudes >1, move one month at a time...
18098 for (var i=0; i<mag; i++) {
18099 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18100 new_date = this.moveMonth(new_date, dir);
18102 // ...then reset the day, keeping it in the new month
18103 new_month = new_date.getUTCMonth();
18104 new_date.setUTCDate(day);
18106 return new_month != new_date.getUTCMonth();
18109 // Common date-resetting loop -- if date is beyond end of month, make it
18112 new_date.setUTCDate(--day);
18113 new_date.setUTCMonth(new_month);
18118 moveYear: function(date, dir)
18120 return this.moveMonth(date, dir*12);
18123 dateWithinRange: function(date)
18125 return date >= this.startDate && date <= this.endDate;
18131 this.picker().remove();
18134 validateValue : function(value)
18136 if(value.length < 1) {
18137 if(this.allowBlank){
18143 if(value.length < this.minLength){
18146 if(value.length > this.maxLength){
18150 var vt = Roo.form.VTypes;
18151 if(!vt[this.vtype](value, this)){
18155 if(typeof this.validator == "function"){
18156 var msg = this.validator(value);
18162 if(this.regex && !this.regex.test(value)){
18166 if(typeof(this.parseDate(value)) == 'undefined'){
18170 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18174 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18184 Roo.apply(Roo.bootstrap.DateField, {
18195 html: '<i class="fa fa-arrow-left"/>'
18205 html: '<i class="fa fa-arrow-right"/>'
18247 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18248 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18249 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18250 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18251 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18264 navFnc: 'FullYear',
18269 navFnc: 'FullYear',
18274 Roo.apply(Roo.bootstrap.DateField, {
18278 cls: 'datepicker dropdown-menu roo-dynamic',
18282 cls: 'datepicker-days',
18286 cls: 'table-condensed',
18288 Roo.bootstrap.DateField.head,
18292 Roo.bootstrap.DateField.footer
18299 cls: 'datepicker-months',
18303 cls: 'table-condensed',
18305 Roo.bootstrap.DateField.head,
18306 Roo.bootstrap.DateField.content,
18307 Roo.bootstrap.DateField.footer
18314 cls: 'datepicker-years',
18318 cls: 'table-condensed',
18320 Roo.bootstrap.DateField.head,
18321 Roo.bootstrap.DateField.content,
18322 Roo.bootstrap.DateField.footer
18341 * @class Roo.bootstrap.TimeField
18342 * @extends Roo.bootstrap.Input
18343 * Bootstrap DateField class
18347 * Create a new TimeField
18348 * @param {Object} config The config object
18351 Roo.bootstrap.TimeField = function(config){
18352 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18356 * Fires when this field show.
18357 * @param {Roo.bootstrap.DateField} thisthis
18358 * @param {Mixed} date The date value
18363 * Fires when this field hide.
18364 * @param {Roo.bootstrap.DateField} this
18365 * @param {Mixed} date The date value
18370 * Fires when select a date.
18371 * @param {Roo.bootstrap.DateField} this
18372 * @param {Mixed} date The date value
18378 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18381 * @cfg {String} format
18382 * The default time format string which can be overriden for localization support. The format must be
18383 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18387 onRender: function(ct, position)
18390 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18392 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18394 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18396 this.pop = this.picker().select('>.datepicker-time',true).first();
18397 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18399 this.picker().on('mousedown', this.onMousedown, this);
18400 this.picker().on('click', this.onClick, this);
18402 this.picker().addClass('datepicker-dropdown');
18407 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18408 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18409 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18410 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18411 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18412 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18416 fireKey: function(e){
18417 if (!this.picker().isVisible()){
18418 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18424 e.preventDefault();
18432 this.onTogglePeriod();
18435 this.onIncrementMinutes();
18438 this.onDecrementMinutes();
18447 onClick: function(e) {
18448 e.stopPropagation();
18449 e.preventDefault();
18452 picker : function()
18454 return this.el.select('.datepicker', true).first();
18457 fillTime: function()
18459 var time = this.pop.select('tbody', true).first();
18461 time.dom.innerHTML = '';
18476 cls: 'hours-up glyphicon glyphicon-chevron-up'
18496 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18517 cls: 'timepicker-hour',
18532 cls: 'timepicker-minute',
18547 cls: 'btn btn-primary period',
18569 cls: 'hours-down glyphicon glyphicon-chevron-down'
18589 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18607 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18614 var hours = this.time.getHours();
18615 var minutes = this.time.getMinutes();
18628 hours = hours - 12;
18632 hours = '0' + hours;
18636 minutes = '0' + minutes;
18639 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18640 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18641 this.pop.select('button', true).first().dom.innerHTML = period;
18647 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18649 var cls = ['bottom'];
18651 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18658 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18663 this.picker().addClass(cls.join('-'));
18667 Roo.each(cls, function(c){
18669 _this.picker().setTop(_this.inputEl().getHeight());
18673 _this.picker().setTop(0 - _this.picker().getHeight());
18678 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18682 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18689 onFocus : function()
18691 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18695 onBlur : function()
18697 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18703 this.picker().show();
18708 this.fireEvent('show', this, this.date);
18713 this.picker().hide();
18716 this.fireEvent('hide', this, this.date);
18719 setTime : function()
18722 this.setValue(this.time.format(this.format));
18724 this.fireEvent('select', this, this.date);
18729 onMousedown: function(e){
18730 e.stopPropagation();
18731 e.preventDefault();
18734 onIncrementHours: function()
18736 Roo.log('onIncrementHours');
18737 this.time = this.time.add(Date.HOUR, 1);
18742 onDecrementHours: function()
18744 Roo.log('onDecrementHours');
18745 this.time = this.time.add(Date.HOUR, -1);
18749 onIncrementMinutes: function()
18751 Roo.log('onIncrementMinutes');
18752 this.time = this.time.add(Date.MINUTE, 1);
18756 onDecrementMinutes: function()
18758 Roo.log('onDecrementMinutes');
18759 this.time = this.time.add(Date.MINUTE, -1);
18763 onTogglePeriod: function()
18765 Roo.log('onTogglePeriod');
18766 this.time = this.time.add(Date.HOUR, 12);
18773 Roo.apply(Roo.bootstrap.TimeField, {
18803 cls: 'btn btn-info ok',
18815 Roo.apply(Roo.bootstrap.TimeField, {
18819 cls: 'datepicker dropdown-menu',
18823 cls: 'datepicker-time',
18827 cls: 'table-condensed',
18829 Roo.bootstrap.TimeField.content,
18830 Roo.bootstrap.TimeField.footer
18849 * @class Roo.bootstrap.MonthField
18850 * @extends Roo.bootstrap.Input
18851 * Bootstrap MonthField class
18853 * @cfg {String} language default en
18856 * Create a new MonthField
18857 * @param {Object} config The config object
18860 Roo.bootstrap.MonthField = function(config){
18861 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18866 * Fires when this field show.
18867 * @param {Roo.bootstrap.MonthField} this
18868 * @param {Mixed} date The date value
18873 * Fires when this field hide.
18874 * @param {Roo.bootstrap.MonthField} this
18875 * @param {Mixed} date The date value
18880 * Fires when select a date.
18881 * @param {Roo.bootstrap.MonthField} this
18882 * @param {String} oldvalue The old value
18883 * @param {String} newvalue The new value
18889 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18891 onRender: function(ct, position)
18894 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18896 this.language = this.language || 'en';
18897 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18898 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18900 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18901 this.isInline = false;
18902 this.isInput = true;
18903 this.component = this.el.select('.add-on', true).first() || false;
18904 this.component = (this.component && this.component.length === 0) ? false : this.component;
18905 this.hasInput = this.component && this.inputEL().length;
18907 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18909 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18911 this.picker().on('mousedown', this.onMousedown, this);
18912 this.picker().on('click', this.onClick, this);
18914 this.picker().addClass('datepicker-dropdown');
18916 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18917 v.setStyle('width', '189px');
18924 if(this.isInline) {
18930 setValue: function(v, suppressEvent)
18932 var o = this.getValue();
18934 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18938 if(suppressEvent !== true){
18939 this.fireEvent('select', this, o, v);
18944 getValue: function()
18949 onClick: function(e)
18951 e.stopPropagation();
18952 e.preventDefault();
18954 var target = e.getTarget();
18956 if(target.nodeName.toLowerCase() === 'i'){
18957 target = Roo.get(target).dom.parentNode;
18960 var nodeName = target.nodeName;
18961 var className = target.className;
18962 var html = target.innerHTML;
18964 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18968 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18970 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18976 picker : function()
18978 return this.pickerEl;
18981 fillMonths: function()
18984 var months = this.picker().select('>.datepicker-months td', true).first();
18986 months.dom.innerHTML = '';
18992 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18995 months.createChild(month);
19004 if(typeof(this.vIndex) == 'undefined' && this.value.length){
19005 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19008 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19009 e.removeClass('active');
19011 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19012 e.addClass('active');
19019 if(this.isInline) {
19023 this.picker().removeClass(['bottom', 'top']);
19025 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19027 * place to the top of element!
19031 this.picker().addClass('top');
19032 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19037 this.picker().addClass('bottom');
19039 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19042 onFocus : function()
19044 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19048 onBlur : function()
19050 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19052 var d = this.inputEl().getValue();
19061 this.picker().show();
19062 this.picker().select('>.datepicker-months', true).first().show();
19066 this.fireEvent('show', this, this.date);
19071 if(this.isInline) {
19074 this.picker().hide();
19075 this.fireEvent('hide', this, this.date);
19079 onMousedown: function(e)
19081 e.stopPropagation();
19082 e.preventDefault();
19087 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19091 fireKey: function(e)
19093 if (!this.picker().isVisible()){
19094 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19105 e.preventDefault();
19109 dir = e.keyCode == 37 ? -1 : 1;
19111 this.vIndex = this.vIndex + dir;
19113 if(this.vIndex < 0){
19117 if(this.vIndex > 11){
19121 if(isNaN(this.vIndex)){
19125 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19131 dir = e.keyCode == 38 ? -1 : 1;
19133 this.vIndex = this.vIndex + dir * 4;
19135 if(this.vIndex < 0){
19139 if(this.vIndex > 11){
19143 if(isNaN(this.vIndex)){
19147 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19152 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19153 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19157 e.preventDefault();
19160 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19161 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19177 this.picker().remove();
19182 Roo.apply(Roo.bootstrap.MonthField, {
19201 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19202 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19207 Roo.apply(Roo.bootstrap.MonthField, {
19211 cls: 'datepicker dropdown-menu roo-dynamic',
19215 cls: 'datepicker-months',
19219 cls: 'table-condensed',
19221 Roo.bootstrap.DateField.content
19241 * @class Roo.bootstrap.CheckBox
19242 * @extends Roo.bootstrap.Input
19243 * Bootstrap CheckBox class
19245 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19246 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19247 * @cfg {String} boxLabel The text that appears beside the checkbox
19248 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19249 * @cfg {Boolean} checked initnal the element
19250 * @cfg {Boolean} inline inline the element (default false)
19251 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19254 * Create a new CheckBox
19255 * @param {Object} config The config object
19258 Roo.bootstrap.CheckBox = function(config){
19259 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19264 * Fires when the element is checked or unchecked.
19265 * @param {Roo.bootstrap.CheckBox} this This input
19266 * @param {Boolean} checked The new checked value
19273 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
19275 inputType: 'checkbox',
19283 getAutoCreate : function()
19285 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19291 cfg.cls = 'form-group ' + this.inputType; //input-group
19294 cfg.cls += ' ' + this.inputType + '-inline';
19300 type : this.inputType,
19301 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
19302 cls : 'roo-' + this.inputType, //'form-box',
19303 placeholder : this.placeholder || ''
19307 if (this.weight) { // Validity check?
19308 cfg.cls += " " + this.inputType + "-" + this.weight;
19311 if (this.disabled) {
19312 input.disabled=true;
19316 input.checked = this.checked;
19320 input.name = this.name;
19324 input.cls += ' input-' + this.size;
19329 ['xs','sm','md','lg'].map(function(size){
19330 if (settings[size]) {
19331 cfg.cls += ' col-' + size + '-' + settings[size];
19335 var inputblock = input;
19337 if (this.before || this.after) {
19340 cls : 'input-group',
19345 inputblock.cn.push({
19347 cls : 'input-group-addon',
19352 inputblock.cn.push(input);
19355 inputblock.cn.push({
19357 cls : 'input-group-addon',
19364 if (align ==='left' && this.fieldLabel.length) {
19365 // Roo.log("left and has label");
19371 cls : 'control-label col-md-' + this.labelWidth,
19372 html : this.fieldLabel
19376 cls : "col-md-" + (12 - this.labelWidth),
19383 } else if ( this.fieldLabel.length) {
19384 // Roo.log(" label");
19388 tag: this.boxLabel ? 'span' : 'label',
19390 cls: 'control-label box-input-label',
19391 //cls : 'input-group-addon',
19392 html : this.fieldLabel
19402 // Roo.log(" no label && no align");
19403 cfg.cn = [ inputblock ] ;
19409 var boxLabelCfg = {
19411 //'for': id, // box label is handled by onclick - so no for...
19413 html: this.boxLabel
19417 boxLabelCfg.tooltip = this.tooltip;
19420 cfg.cn.push(boxLabelCfg);
19430 * return the real input element.
19432 inputEl: function ()
19434 return this.el.select('input.roo-' + this.inputType,true).first();
19437 labelEl: function()
19439 return this.el.select('label.control-label',true).first();
19441 /* depricated... */
19445 return this.labelEl();
19448 boxLabelEl: function()
19450 return this.el.select('label.box-label',true).first();
19453 initEvents : function()
19455 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19457 this.inputEl().on('click', this.onClick, this);
19459 if (this.boxLabel) {
19460 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19463 this.startValue = this.getValue();
19466 Roo.bootstrap.CheckBox.register(this);
19470 onClick : function()
19472 this.setChecked(!this.checked);
19475 setChecked : function(state,suppressEvent)
19477 this.startValue = this.getValue();
19479 if(this.inputType == 'radio'){
19481 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19482 e.dom.checked = false;
19485 this.inputEl().dom.checked = true;
19487 this.inputEl().dom.value = this.inputValue;
19489 if(suppressEvent !== true){
19490 this.fireEvent('check', this, true);
19498 this.checked = state;
19500 this.inputEl().dom.checked = state;
19502 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19504 if(suppressEvent !== true){
19505 this.fireEvent('check', this, state);
19511 getValue : function()
19513 if(this.inputType == 'radio'){
19514 return this.getGroupValue();
19517 return this.inputEl().getValue();
19521 getGroupValue : function()
19523 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19527 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19530 setValue : function(v,suppressEvent)
19532 if(this.inputType == 'radio'){
19533 this.setGroupValue(v, suppressEvent);
19537 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19542 setGroupValue : function(v, suppressEvent)
19544 this.startValue = this.getValue();
19546 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19547 e.dom.checked = false;
19549 if(e.dom.value == v){
19550 e.dom.checked = true;
19554 if(suppressEvent !== true){
19555 this.fireEvent('check', this, true);
19563 validate : function()
19567 (this.inputType == 'radio' && this.validateRadio()) ||
19568 (this.inputType == 'checkbox' && this.validateCheckbox())
19574 this.markInvalid();
19578 validateRadio : function()
19580 if(this.allowBlank){
19586 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19587 if(!e.dom.checked){
19599 validateCheckbox : function()
19602 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19605 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19613 for(var i in group){
19618 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19625 * Mark this field as valid
19627 markValid : function()
19629 if(this.allowBlank){
19635 this.fireEvent('valid', this);
19637 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19640 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19647 if(this.inputType == 'radio'){
19648 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19649 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19650 e.findParent('.form-group', false, true).addClass(_this.validClass);
19657 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19658 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19662 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19668 for(var i in group){
19669 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19670 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19675 * Mark this field as invalid
19676 * @param {String} msg The validation message
19678 markInvalid : function(msg)
19680 if(this.allowBlank){
19686 this.fireEvent('invalid', this, msg);
19688 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19691 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19695 label.markInvalid();
19698 if(this.inputType == 'radio'){
19699 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19700 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19701 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19708 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19709 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19713 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19719 for(var i in group){
19720 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19721 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19726 disable : function()
19728 if(this.inputType != 'radio'){
19729 Roo.bootstrap.CheckBox.superclass.disable.call(this);
19736 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19737 _this.getActionEl().addClass(this.disabledClass);
19738 e.dom.disabled = true;
19742 this.disabled = true;
19743 this.fireEvent("disable", this);
19747 enable : function()
19749 if(this.inputType != 'radio'){
19750 Roo.bootstrap.CheckBox.superclass.enable.call(this);
19757 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19758 _this.getActionEl().removeClass(this.disabledClass);
19759 e.dom.disabled = false;
19763 this.disabled = false;
19764 this.fireEvent("enable", this);
19771 Roo.apply(Roo.bootstrap.CheckBox, {
19776 * register a CheckBox Group
19777 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19779 register : function(checkbox)
19781 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19782 this.groups[checkbox.groupId] = {};
19785 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19789 this.groups[checkbox.groupId][checkbox.name] = checkbox;
19793 * fetch a CheckBox Group based on the group ID
19794 * @param {string} the group ID
19795 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19797 get: function(groupId) {
19798 if (typeof(this.groups[groupId]) == 'undefined') {
19802 return this.groups[groupId] ;
19814 *<div class="radio">
19816 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19817 Option one is this and that—be sure to include why it's great
19824 *<label class="radio-inline">fieldLabel</label>
19825 *<label class="radio-inline">
19826 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19834 * @class Roo.bootstrap.Radio
19835 * @extends Roo.bootstrap.CheckBox
19836 * Bootstrap Radio class
19839 * Create a new Radio
19840 * @param {Object} config The config object
19843 Roo.bootstrap.Radio = function(config){
19844 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19848 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19850 inputType: 'radio',
19854 getAutoCreate : function()
19856 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19857 align = align || 'left'; // default...
19864 tag : this.inline ? 'span' : 'div',
19869 var inline = this.inline ? ' radio-inline' : '';
19873 // does not need for, as we wrap the input with it..
19875 cls : 'control-label box-label' + inline,
19878 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19882 //cls : 'control-label' + inline,
19883 html : this.fieldLabel,
19884 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19893 type : this.inputType,
19894 //value : (!this.checked) ? this.valueOff : this.inputValue,
19895 value : this.inputValue,
19897 placeholder : this.placeholder || '' // ?? needed????
19900 if (this.weight) { // Validity check?
19901 input.cls += " radio-" + this.weight;
19903 if (this.disabled) {
19904 input.disabled=true;
19908 input.checked = this.checked;
19912 input.name = this.name;
19916 input.cls += ' input-' + this.size;
19919 //?? can span's inline have a width??
19922 ['xs','sm','md','lg'].map(function(size){
19923 if (settings[size]) {
19924 cfg.cls += ' col-' + size + '-' + settings[size];
19928 var inputblock = input;
19930 if (this.before || this.after) {
19933 cls : 'input-group',
19938 inputblock.cn.push({
19940 cls : 'input-group-addon',
19944 inputblock.cn.push(input);
19946 inputblock.cn.push({
19948 cls : 'input-group-addon',
19956 if (this.fieldLabel && this.fieldLabel.length) {
19957 cfg.cn.push(fieldLabel);
19960 // normal bootstrap puts the input inside the label.
19961 // however with our styled version - it has to go after the input.
19963 //lbl.cn.push(inputblock);
19967 cls: 'radio' + inline,
19974 cfg.cn.push( lblwrap);
19979 html: this.boxLabel
19988 initEvents : function()
19990 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19992 this.inputEl().on('click', this.onClick, this);
19993 if (this.boxLabel) {
19994 //Roo.log('find label');
19995 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
20000 inputEl: function ()
20002 return this.el.select('input.roo-radio',true).first();
20004 onClick : function()
20007 this.setChecked(true);
20010 setChecked : function(state,suppressEvent)
20013 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20014 v.dom.checked = false;
20017 Roo.log(this.inputEl().dom);
20018 this.checked = state;
20019 this.inputEl().dom.checked = state;
20021 if(suppressEvent !== true){
20022 this.fireEvent('check', this, state);
20025 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20029 getGroupValue : function()
20032 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20033 if(v.dom.checked == true){
20034 value = v.dom.value;
20042 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
20043 * @return {Mixed} value The field value
20045 getValue : function(){
20046 return this.getGroupValue();
20052 //<script type="text/javascript">
20055 * Based Ext JS Library 1.1.1
20056 * Copyright(c) 2006-2007, Ext JS, LLC.
20062 * @class Roo.HtmlEditorCore
20063 * @extends Roo.Component
20064 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20066 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20069 Roo.HtmlEditorCore = function(config){
20072 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20077 * @event initialize
20078 * Fires when the editor is fully initialized (including the iframe)
20079 * @param {Roo.HtmlEditorCore} this
20084 * Fires when the editor is first receives the focus. Any insertion must wait
20085 * until after this event.
20086 * @param {Roo.HtmlEditorCore} this
20090 * @event beforesync
20091 * Fires before the textarea is updated with content from the editor iframe. Return false
20092 * to cancel the sync.
20093 * @param {Roo.HtmlEditorCore} this
20094 * @param {String} html
20098 * @event beforepush
20099 * Fires before the iframe editor is updated with content from the textarea. Return false
20100 * to cancel the push.
20101 * @param {Roo.HtmlEditorCore} this
20102 * @param {String} html
20107 * Fires when the textarea is updated with content from the editor iframe.
20108 * @param {Roo.HtmlEditorCore} this
20109 * @param {String} html
20114 * Fires when the iframe editor is updated with content from the textarea.
20115 * @param {Roo.HtmlEditorCore} this
20116 * @param {String} html
20121 * @event editorevent
20122 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20123 * @param {Roo.HtmlEditorCore} this
20129 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20131 // defaults : white / black...
20132 this.applyBlacklists();
20139 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20143 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20149 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20154 * @cfg {Number} height (in pixels)
20158 * @cfg {Number} width (in pixels)
20163 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20166 stylesheets: false,
20171 // private properties
20172 validationEvent : false,
20174 initialized : false,
20176 sourceEditMode : false,
20177 onFocus : Roo.emptyFn,
20179 hideMode:'offsets',
20183 // blacklist + whitelisted elements..
20190 * Protected method that will not generally be called directly. It
20191 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20192 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20194 getDocMarkup : function(){
20198 // inherit styels from page...??
20199 if (this.stylesheets === false) {
20201 Roo.get(document.head).select('style').each(function(node) {
20202 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20205 Roo.get(document.head).select('link').each(function(node) {
20206 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20209 } else if (!this.stylesheets.length) {
20211 st = '<style type="text/css">' +
20212 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20218 st += '<style type="text/css">' +
20219 'IMG { cursor: pointer } ' +
20223 return '<html><head>' + st +
20224 //<style type="text/css">' +
20225 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20227 ' </head><body class="roo-htmleditor-body"></body></html>';
20231 onRender : function(ct, position)
20234 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20235 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20238 this.el.dom.style.border = '0 none';
20239 this.el.dom.setAttribute('tabIndex', -1);
20240 this.el.addClass('x-hidden hide');
20244 if(Roo.isIE){ // fix IE 1px bogus margin
20245 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20249 this.frameId = Roo.id();
20253 var iframe = this.owner.wrap.createChild({
20255 cls: 'form-control', // bootstrap..
20257 name: this.frameId,
20258 frameBorder : 'no',
20259 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20264 this.iframe = iframe.dom;
20266 this.assignDocWin();
20268 this.doc.designMode = 'on';
20271 this.doc.write(this.getDocMarkup());
20275 var task = { // must defer to wait for browser to be ready
20277 //console.log("run task?" + this.doc.readyState);
20278 this.assignDocWin();
20279 if(this.doc.body || this.doc.readyState == 'complete'){
20281 this.doc.designMode="on";
20285 Roo.TaskMgr.stop(task);
20286 this.initEditor.defer(10, this);
20293 Roo.TaskMgr.start(task);
20298 onResize : function(w, h)
20300 Roo.log('resize: ' +w + ',' + h );
20301 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20305 if(typeof w == 'number'){
20307 this.iframe.style.width = w + 'px';
20309 if(typeof h == 'number'){
20311 this.iframe.style.height = h + 'px';
20313 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20320 * Toggles the editor between standard and source edit mode.
20321 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20323 toggleSourceEdit : function(sourceEditMode){
20325 this.sourceEditMode = sourceEditMode === true;
20327 if(this.sourceEditMode){
20329 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20332 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20333 //this.iframe.className = '';
20336 //this.setSize(this.owner.wrap.getSize());
20337 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20344 * Protected method that will not generally be called directly. If you need/want
20345 * custom HTML cleanup, this is the method you should override.
20346 * @param {String} html The HTML to be cleaned
20347 * return {String} The cleaned HTML
20349 cleanHtml : function(html){
20350 html = String(html);
20351 if(html.length > 5){
20352 if(Roo.isSafari){ // strip safari nonsense
20353 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20356 if(html == ' '){
20363 * HTML Editor -> Textarea
20364 * Protected method that will not generally be called directly. Syncs the contents
20365 * of the editor iframe with the textarea.
20367 syncValue : function(){
20368 if(this.initialized){
20369 var bd = (this.doc.body || this.doc.documentElement);
20370 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20371 var html = bd.innerHTML;
20373 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20374 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20376 html = '<div style="'+m[0]+'">' + html + '</div>';
20379 html = this.cleanHtml(html);
20380 // fix up the special chars.. normaly like back quotes in word...
20381 // however we do not want to do this with chinese..
20382 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20383 var cc = b.charCodeAt();
20385 (cc >= 0x4E00 && cc < 0xA000 ) ||
20386 (cc >= 0x3400 && cc < 0x4E00 ) ||
20387 (cc >= 0xf900 && cc < 0xfb00 )
20393 if(this.owner.fireEvent('beforesync', this, html) !== false){
20394 this.el.dom.value = html;
20395 this.owner.fireEvent('sync', this, html);
20401 * Protected method that will not generally be called directly. Pushes the value of the textarea
20402 * into the iframe editor.
20404 pushValue : function(){
20405 if(this.initialized){
20406 var v = this.el.dom.value.trim();
20408 // if(v.length < 1){
20412 if(this.owner.fireEvent('beforepush', this, v) !== false){
20413 var d = (this.doc.body || this.doc.documentElement);
20415 this.cleanUpPaste();
20416 this.el.dom.value = d.innerHTML;
20417 this.owner.fireEvent('push', this, v);
20423 deferFocus : function(){
20424 this.focus.defer(10, this);
20428 focus : function(){
20429 if(this.win && !this.sourceEditMode){
20436 assignDocWin: function()
20438 var iframe = this.iframe;
20441 this.doc = iframe.contentWindow.document;
20442 this.win = iframe.contentWindow;
20444 // if (!Roo.get(this.frameId)) {
20447 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20448 // this.win = Roo.get(this.frameId).dom.contentWindow;
20450 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20454 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20455 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20460 initEditor : function(){
20461 //console.log("INIT EDITOR");
20462 this.assignDocWin();
20466 this.doc.designMode="on";
20468 this.doc.write(this.getDocMarkup());
20471 var dbody = (this.doc.body || this.doc.documentElement);
20472 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20473 // this copies styles from the containing element into thsi one..
20474 // not sure why we need all of this..
20475 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20477 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20478 //ss['background-attachment'] = 'fixed'; // w3c
20479 dbody.bgProperties = 'fixed'; // ie
20480 //Roo.DomHelper.applyStyles(dbody, ss);
20481 Roo.EventManager.on(this.doc, {
20482 //'mousedown': this.onEditorEvent,
20483 'mouseup': this.onEditorEvent,
20484 'dblclick': this.onEditorEvent,
20485 'click': this.onEditorEvent,
20486 'keyup': this.onEditorEvent,
20491 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20493 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20494 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20496 this.initialized = true;
20498 this.owner.fireEvent('initialize', this);
20503 onDestroy : function(){
20509 //for (var i =0; i < this.toolbars.length;i++) {
20510 // // fixme - ask toolbars for heights?
20511 // this.toolbars[i].onDestroy();
20514 //this.wrap.dom.innerHTML = '';
20515 //this.wrap.remove();
20520 onFirstFocus : function(){
20522 this.assignDocWin();
20525 this.activated = true;
20528 if(Roo.isGecko){ // prevent silly gecko errors
20530 var s = this.win.getSelection();
20531 if(!s.focusNode || s.focusNode.nodeType != 3){
20532 var r = s.getRangeAt(0);
20533 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20538 this.execCmd('useCSS', true);
20539 this.execCmd('styleWithCSS', false);
20542 this.owner.fireEvent('activate', this);
20546 adjustFont: function(btn){
20547 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20548 //if(Roo.isSafari){ // safari
20551 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20552 if(Roo.isSafari){ // safari
20553 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20554 v = (v < 10) ? 10 : v;
20555 v = (v > 48) ? 48 : v;
20556 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20561 v = Math.max(1, v+adjust);
20563 this.execCmd('FontSize', v );
20566 onEditorEvent : function(e)
20568 this.owner.fireEvent('editorevent', this, e);
20569 // this.updateToolbar();
20570 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20573 insertTag : function(tg)
20575 // could be a bit smarter... -> wrap the current selected tRoo..
20576 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20578 range = this.createRange(this.getSelection());
20579 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20580 wrappingNode.appendChild(range.extractContents());
20581 range.insertNode(wrappingNode);
20588 this.execCmd("formatblock", tg);
20592 insertText : function(txt)
20596 var range = this.createRange();
20597 range.deleteContents();
20598 //alert(Sender.getAttribute('label'));
20600 range.insertNode(this.doc.createTextNode(txt));
20606 * Executes a Midas editor command on the editor document and performs necessary focus and
20607 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20608 * @param {String} cmd The Midas command
20609 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20611 relayCmd : function(cmd, value){
20613 this.execCmd(cmd, value);
20614 this.owner.fireEvent('editorevent', this);
20615 //this.updateToolbar();
20616 this.owner.deferFocus();
20620 * Executes a Midas editor command directly on the editor document.
20621 * For visual commands, you should use {@link #relayCmd} instead.
20622 * <b>This should only be called after the editor is initialized.</b>
20623 * @param {String} cmd The Midas command
20624 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20626 execCmd : function(cmd, value){
20627 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20634 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20636 * @param {String} text | dom node..
20638 insertAtCursor : function(text)
20643 if(!this.activated){
20649 var r = this.doc.selection.createRange();
20660 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20664 // from jquery ui (MIT licenced)
20666 var win = this.win;
20668 if (win.getSelection && win.getSelection().getRangeAt) {
20669 range = win.getSelection().getRangeAt(0);
20670 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20671 range.insertNode(node);
20672 } else if (win.document.selection && win.document.selection.createRange) {
20673 // no firefox support
20674 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20675 win.document.selection.createRange().pasteHTML(txt);
20677 // no firefox support
20678 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20679 this.execCmd('InsertHTML', txt);
20688 mozKeyPress : function(e){
20690 var c = e.getCharCode(), cmd;
20693 c = String.fromCharCode(c).toLowerCase();
20707 this.cleanUpPaste.defer(100, this);
20715 e.preventDefault();
20723 fixKeys : function(){ // load time branching for fastest keydown performance
20725 return function(e){
20726 var k = e.getKey(), r;
20729 r = this.doc.selection.createRange();
20732 r.pasteHTML('    ');
20739 r = this.doc.selection.createRange();
20741 var target = r.parentElement();
20742 if(!target || target.tagName.toLowerCase() != 'li'){
20744 r.pasteHTML('<br />');
20750 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20751 this.cleanUpPaste.defer(100, this);
20757 }else if(Roo.isOpera){
20758 return function(e){
20759 var k = e.getKey();
20763 this.execCmd('InsertHTML','    ');
20766 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20767 this.cleanUpPaste.defer(100, this);
20772 }else if(Roo.isSafari){
20773 return function(e){
20774 var k = e.getKey();
20778 this.execCmd('InsertText','\t');
20782 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20783 this.cleanUpPaste.defer(100, this);
20791 getAllAncestors: function()
20793 var p = this.getSelectedNode();
20796 a.push(p); // push blank onto stack..
20797 p = this.getParentElement();
20801 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20805 a.push(this.doc.body);
20809 lastSelNode : false,
20812 getSelection : function()
20814 this.assignDocWin();
20815 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20818 getSelectedNode: function()
20820 // this may only work on Gecko!!!
20822 // should we cache this!!!!
20827 var range = this.createRange(this.getSelection()).cloneRange();
20830 var parent = range.parentElement();
20832 var testRange = range.duplicate();
20833 testRange.moveToElementText(parent);
20834 if (testRange.inRange(range)) {
20837 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20840 parent = parent.parentElement;
20845 // is ancestor a text element.
20846 var ac = range.commonAncestorContainer;
20847 if (ac.nodeType == 3) {
20848 ac = ac.parentNode;
20851 var ar = ac.childNodes;
20854 var other_nodes = [];
20855 var has_other_nodes = false;
20856 for (var i=0;i<ar.length;i++) {
20857 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20860 // fullly contained node.
20862 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20867 // probably selected..
20868 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20869 other_nodes.push(ar[i]);
20873 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20878 has_other_nodes = true;
20880 if (!nodes.length && other_nodes.length) {
20881 nodes= other_nodes;
20883 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20889 createRange: function(sel)
20891 // this has strange effects when using with
20892 // top toolbar - not sure if it's a great idea.
20893 //this.editor.contentWindow.focus();
20894 if (typeof sel != "undefined") {
20896 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20898 return this.doc.createRange();
20901 return this.doc.createRange();
20904 getParentElement: function()
20907 this.assignDocWin();
20908 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20910 var range = this.createRange(sel);
20913 var p = range.commonAncestorContainer;
20914 while (p.nodeType == 3) { // text node
20925 * Range intersection.. the hard stuff...
20929 * [ -- selected range --- ]
20933 * if end is before start or hits it. fail.
20934 * if start is after end or hits it fail.
20936 * if either hits (but other is outside. - then it's not
20942 // @see http://www.thismuchiknow.co.uk/?p=64.
20943 rangeIntersectsNode : function(range, node)
20945 var nodeRange = node.ownerDocument.createRange();
20947 nodeRange.selectNode(node);
20949 nodeRange.selectNodeContents(node);
20952 var rangeStartRange = range.cloneRange();
20953 rangeStartRange.collapse(true);
20955 var rangeEndRange = range.cloneRange();
20956 rangeEndRange.collapse(false);
20958 var nodeStartRange = nodeRange.cloneRange();
20959 nodeStartRange.collapse(true);
20961 var nodeEndRange = nodeRange.cloneRange();
20962 nodeEndRange.collapse(false);
20964 return rangeStartRange.compareBoundaryPoints(
20965 Range.START_TO_START, nodeEndRange) == -1 &&
20966 rangeEndRange.compareBoundaryPoints(
20967 Range.START_TO_START, nodeStartRange) == 1;
20971 rangeCompareNode : function(range, node)
20973 var nodeRange = node.ownerDocument.createRange();
20975 nodeRange.selectNode(node);
20977 nodeRange.selectNodeContents(node);
20981 range.collapse(true);
20983 nodeRange.collapse(true);
20985 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20986 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20988 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20990 var nodeIsBefore = ss == 1;
20991 var nodeIsAfter = ee == -1;
20993 if (nodeIsBefore && nodeIsAfter) {
20996 if (!nodeIsBefore && nodeIsAfter) {
20997 return 1; //right trailed.
21000 if (nodeIsBefore && !nodeIsAfter) {
21001 return 2; // left trailed.
21007 // private? - in a new class?
21008 cleanUpPaste : function()
21010 // cleans up the whole document..
21011 Roo.log('cleanuppaste');
21013 this.cleanUpChildren(this.doc.body);
21014 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21015 if (clean != this.doc.body.innerHTML) {
21016 this.doc.body.innerHTML = clean;
21021 cleanWordChars : function(input) {// change the chars to hex code
21022 var he = Roo.HtmlEditorCore;
21024 var output = input;
21025 Roo.each(he.swapCodes, function(sw) {
21026 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21028 output = output.replace(swapper, sw[1]);
21035 cleanUpChildren : function (n)
21037 if (!n.childNodes.length) {
21040 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21041 this.cleanUpChild(n.childNodes[i]);
21048 cleanUpChild : function (node)
21051 //console.log(node);
21052 if (node.nodeName == "#text") {
21053 // clean up silly Windows -- stuff?
21056 if (node.nodeName == "#comment") {
21057 node.parentNode.removeChild(node);
21058 // clean up silly Windows -- stuff?
21061 var lcname = node.tagName.toLowerCase();
21062 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21063 // whitelist of tags..
21065 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21067 node.parentNode.removeChild(node);
21072 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21074 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21075 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21077 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21078 // remove_keep_children = true;
21081 if (remove_keep_children) {
21082 this.cleanUpChildren(node);
21083 // inserts everything just before this node...
21084 while (node.childNodes.length) {
21085 var cn = node.childNodes[0];
21086 node.removeChild(cn);
21087 node.parentNode.insertBefore(cn, node);
21089 node.parentNode.removeChild(node);
21093 if (!node.attributes || !node.attributes.length) {
21094 this.cleanUpChildren(node);
21098 function cleanAttr(n,v)
21101 if (v.match(/^\./) || v.match(/^\//)) {
21104 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21107 if (v.match(/^#/)) {
21110 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21111 node.removeAttribute(n);
21115 var cwhite = this.cwhite;
21116 var cblack = this.cblack;
21118 function cleanStyle(n,v)
21120 if (v.match(/expression/)) { //XSS?? should we even bother..
21121 node.removeAttribute(n);
21125 var parts = v.split(/;/);
21128 Roo.each(parts, function(p) {
21129 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21133 var l = p.split(':').shift().replace(/\s+/g,'');
21134 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21136 if ( cwhite.length && cblack.indexOf(l) > -1) {
21137 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21138 //node.removeAttribute(n);
21142 // only allow 'c whitelisted system attributes'
21143 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21144 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21145 //node.removeAttribute(n);
21155 if (clean.length) {
21156 node.setAttribute(n, clean.join(';'));
21158 node.removeAttribute(n);
21164 for (var i = node.attributes.length-1; i > -1 ; i--) {
21165 var a = node.attributes[i];
21168 if (a.name.toLowerCase().substr(0,2)=='on') {
21169 node.removeAttribute(a.name);
21172 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21173 node.removeAttribute(a.name);
21176 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21177 cleanAttr(a.name,a.value); // fixme..
21180 if (a.name == 'style') {
21181 cleanStyle(a.name,a.value);
21184 /// clean up MS crap..
21185 // tecnically this should be a list of valid class'es..
21188 if (a.name == 'class') {
21189 if (a.value.match(/^Mso/)) {
21190 node.className = '';
21193 if (a.value.match(/body/)) {
21194 node.className = '';
21205 this.cleanUpChildren(node);
21211 * Clean up MS wordisms...
21213 cleanWord : function(node)
21218 this.cleanWord(this.doc.body);
21221 if (node.nodeName == "#text") {
21222 // clean up silly Windows -- stuff?
21225 if (node.nodeName == "#comment") {
21226 node.parentNode.removeChild(node);
21227 // clean up silly Windows -- stuff?
21231 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21232 node.parentNode.removeChild(node);
21236 // remove - but keep children..
21237 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21238 while (node.childNodes.length) {
21239 var cn = node.childNodes[0];
21240 node.removeChild(cn);
21241 node.parentNode.insertBefore(cn, node);
21243 node.parentNode.removeChild(node);
21244 this.iterateChildren(node, this.cleanWord);
21248 if (node.className.length) {
21250 var cn = node.className.split(/\W+/);
21252 Roo.each(cn, function(cls) {
21253 if (cls.match(/Mso[a-zA-Z]+/)) {
21258 node.className = cna.length ? cna.join(' ') : '';
21260 node.removeAttribute("class");
21264 if (node.hasAttribute("lang")) {
21265 node.removeAttribute("lang");
21268 if (node.hasAttribute("style")) {
21270 var styles = node.getAttribute("style").split(";");
21272 Roo.each(styles, function(s) {
21273 if (!s.match(/:/)) {
21276 var kv = s.split(":");
21277 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21280 // what ever is left... we allow.
21283 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21284 if (!nstyle.length) {
21285 node.removeAttribute('style');
21288 this.iterateChildren(node, this.cleanWord);
21294 * iterateChildren of a Node, calling fn each time, using this as the scole..
21295 * @param {DomNode} node node to iterate children of.
21296 * @param {Function} fn method of this class to call on each item.
21298 iterateChildren : function(node, fn)
21300 if (!node.childNodes.length) {
21303 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21304 fn.call(this, node.childNodes[i])
21310 * cleanTableWidths.
21312 * Quite often pasting from word etc.. results in tables with column and widths.
21313 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21316 cleanTableWidths : function(node)
21321 this.cleanTableWidths(this.doc.body);
21326 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21329 Roo.log(node.tagName);
21330 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21331 this.iterateChildren(node, this.cleanTableWidths);
21334 if (node.hasAttribute('width')) {
21335 node.removeAttribute('width');
21339 if (node.hasAttribute("style")) {
21342 var styles = node.getAttribute("style").split(";");
21344 Roo.each(styles, function(s) {
21345 if (!s.match(/:/)) {
21348 var kv = s.split(":");
21349 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21352 // what ever is left... we allow.
21355 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21356 if (!nstyle.length) {
21357 node.removeAttribute('style');
21361 this.iterateChildren(node, this.cleanTableWidths);
21369 domToHTML : function(currentElement, depth, nopadtext) {
21371 depth = depth || 0;
21372 nopadtext = nopadtext || false;
21374 if (!currentElement) {
21375 return this.domToHTML(this.doc.body);
21378 //Roo.log(currentElement);
21380 var allText = false;
21381 var nodeName = currentElement.nodeName;
21382 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21384 if (nodeName == '#text') {
21386 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21391 if (nodeName != 'BODY') {
21394 // Prints the node tagName, such as <A>, <IMG>, etc
21397 for(i = 0; i < currentElement.attributes.length;i++) {
21399 var aname = currentElement.attributes.item(i).name;
21400 if (!currentElement.attributes.item(i).value.length) {
21403 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21406 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21415 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21418 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21423 // Traverse the tree
21425 var currentElementChild = currentElement.childNodes.item(i);
21426 var allText = true;
21427 var innerHTML = '';
21429 while (currentElementChild) {
21430 // Formatting code (indent the tree so it looks nice on the screen)
21431 var nopad = nopadtext;
21432 if (lastnode == 'SPAN') {
21436 if (currentElementChild.nodeName == '#text') {
21437 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21438 toadd = nopadtext ? toadd : toadd.trim();
21439 if (!nopad && toadd.length > 80) {
21440 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21442 innerHTML += toadd;
21445 currentElementChild = currentElement.childNodes.item(i);
21451 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21453 // Recursively traverse the tree structure of the child node
21454 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21455 lastnode = currentElementChild.nodeName;
21457 currentElementChild=currentElement.childNodes.item(i);
21463 // The remaining code is mostly for formatting the tree
21464 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21469 ret+= "</"+tagName+">";
21475 applyBlacklists : function()
21477 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21478 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21482 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21483 if (b.indexOf(tag) > -1) {
21486 this.white.push(tag);
21490 Roo.each(w, function(tag) {
21491 if (b.indexOf(tag) > -1) {
21494 if (this.white.indexOf(tag) > -1) {
21497 this.white.push(tag);
21502 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21503 if (w.indexOf(tag) > -1) {
21506 this.black.push(tag);
21510 Roo.each(b, function(tag) {
21511 if (w.indexOf(tag) > -1) {
21514 if (this.black.indexOf(tag) > -1) {
21517 this.black.push(tag);
21522 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21523 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21527 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21528 if (b.indexOf(tag) > -1) {
21531 this.cwhite.push(tag);
21535 Roo.each(w, function(tag) {
21536 if (b.indexOf(tag) > -1) {
21539 if (this.cwhite.indexOf(tag) > -1) {
21542 this.cwhite.push(tag);
21547 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21548 if (w.indexOf(tag) > -1) {
21551 this.cblack.push(tag);
21555 Roo.each(b, function(tag) {
21556 if (w.indexOf(tag) > -1) {
21559 if (this.cblack.indexOf(tag) > -1) {
21562 this.cblack.push(tag);
21567 setStylesheets : function(stylesheets)
21569 if(typeof(stylesheets) == 'string'){
21570 Roo.get(this.iframe.contentDocument.head).createChild({
21572 rel : 'stylesheet',
21581 Roo.each(stylesheets, function(s) {
21586 Roo.get(_this.iframe.contentDocument.head).createChild({
21588 rel : 'stylesheet',
21597 removeStylesheets : function()
21601 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21606 // hide stuff that is not compatible
21620 * @event specialkey
21624 * @cfg {String} fieldClass @hide
21627 * @cfg {String} focusClass @hide
21630 * @cfg {String} autoCreate @hide
21633 * @cfg {String} inputType @hide
21636 * @cfg {String} invalidClass @hide
21639 * @cfg {String} invalidText @hide
21642 * @cfg {String} msgFx @hide
21645 * @cfg {String} validateOnBlur @hide
21649 Roo.HtmlEditorCore.white = [
21650 'area', 'br', 'img', 'input', 'hr', 'wbr',
21652 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21653 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21654 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21655 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21656 'table', 'ul', 'xmp',
21658 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21661 'dir', 'menu', 'ol', 'ul', 'dl',
21667 Roo.HtmlEditorCore.black = [
21668 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21670 'base', 'basefont', 'bgsound', 'blink', 'body',
21671 'frame', 'frameset', 'head', 'html', 'ilayer',
21672 'iframe', 'layer', 'link', 'meta', 'object',
21673 'script', 'style' ,'title', 'xml' // clean later..
21675 Roo.HtmlEditorCore.clean = [
21676 'script', 'style', 'title', 'xml'
21678 Roo.HtmlEditorCore.remove = [
21683 Roo.HtmlEditorCore.ablack = [
21687 Roo.HtmlEditorCore.aclean = [
21688 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21692 Roo.HtmlEditorCore.pwhite= [
21693 'http', 'https', 'mailto'
21696 // white listed style attributes.
21697 Roo.HtmlEditorCore.cwhite= [
21698 // 'text-align', /// default is to allow most things..
21704 // black listed style attributes.
21705 Roo.HtmlEditorCore.cblack= [
21706 // 'font-size' -- this can be set by the project
21710 Roo.HtmlEditorCore.swapCodes =[
21729 * @class Roo.bootstrap.HtmlEditor
21730 * @extends Roo.bootstrap.TextArea
21731 * Bootstrap HtmlEditor class
21734 * Create a new HtmlEditor
21735 * @param {Object} config The config object
21738 Roo.bootstrap.HtmlEditor = function(config){
21739 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21740 if (!this.toolbars) {
21741 this.toolbars = [];
21743 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21746 * @event initialize
21747 * Fires when the editor is fully initialized (including the iframe)
21748 * @param {HtmlEditor} this
21753 * Fires when the editor is first receives the focus. Any insertion must wait
21754 * until after this event.
21755 * @param {HtmlEditor} this
21759 * @event beforesync
21760 * Fires before the textarea is updated with content from the editor iframe. Return false
21761 * to cancel the sync.
21762 * @param {HtmlEditor} this
21763 * @param {String} html
21767 * @event beforepush
21768 * Fires before the iframe editor is updated with content from the textarea. Return false
21769 * to cancel the push.
21770 * @param {HtmlEditor} this
21771 * @param {String} html
21776 * Fires when the textarea is updated with content from the editor iframe.
21777 * @param {HtmlEditor} this
21778 * @param {String} html
21783 * Fires when the iframe editor is updated with content from the textarea.
21784 * @param {HtmlEditor} this
21785 * @param {String} html
21789 * @event editmodechange
21790 * Fires when the editor switches edit modes
21791 * @param {HtmlEditor} this
21792 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21794 editmodechange: true,
21796 * @event editorevent
21797 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21798 * @param {HtmlEditor} this
21802 * @event firstfocus
21803 * Fires when on first focus - needed by toolbars..
21804 * @param {HtmlEditor} this
21809 * Auto save the htmlEditor value as a file into Events
21810 * @param {HtmlEditor} this
21814 * @event savedpreview
21815 * preview the saved version of htmlEditor
21816 * @param {HtmlEditor} this
21823 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21827 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21832 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21837 * @cfg {Number} height (in pixels)
21841 * @cfg {Number} width (in pixels)
21846 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21849 stylesheets: false,
21854 // private properties
21855 validationEvent : false,
21857 initialized : false,
21860 onFocus : Roo.emptyFn,
21862 hideMode:'offsets',
21865 tbContainer : false,
21867 toolbarContainer :function() {
21868 return this.wrap.select('.x-html-editor-tb',true).first();
21872 * Protected method that will not generally be called directly. It
21873 * is called when the editor creates its toolbar. Override this method if you need to
21874 * add custom toolbar buttons.
21875 * @param {HtmlEditor} editor
21877 createToolbar : function(){
21879 Roo.log("create toolbars");
21881 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21882 this.toolbars[0].render(this.toolbarContainer());
21886 // if (!editor.toolbars || !editor.toolbars.length) {
21887 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21890 // for (var i =0 ; i < editor.toolbars.length;i++) {
21891 // editor.toolbars[i] = Roo.factory(
21892 // typeof(editor.toolbars[i]) == 'string' ?
21893 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21894 // Roo.bootstrap.HtmlEditor);
21895 // editor.toolbars[i].init(editor);
21901 onRender : function(ct, position)
21903 // Roo.log("Call onRender: " + this.xtype);
21905 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21907 this.wrap = this.inputEl().wrap({
21908 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21911 this.editorcore.onRender(ct, position);
21913 if (this.resizable) {
21914 this.resizeEl = new Roo.Resizable(this.wrap, {
21918 minHeight : this.height,
21919 height: this.height,
21920 handles : this.resizable,
21923 resize : function(r, w, h) {
21924 _t.onResize(w,h); // -something
21930 this.createToolbar(this);
21933 if(!this.width && this.resizable){
21934 this.setSize(this.wrap.getSize());
21936 if (this.resizeEl) {
21937 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21938 // should trigger onReize..
21944 onResize : function(w, h)
21946 Roo.log('resize: ' +w + ',' + h );
21947 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21951 if(this.inputEl() ){
21952 if(typeof w == 'number'){
21953 var aw = w - this.wrap.getFrameWidth('lr');
21954 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21957 if(typeof h == 'number'){
21958 var tbh = -11; // fixme it needs to tool bar size!
21959 for (var i =0; i < this.toolbars.length;i++) {
21960 // fixme - ask toolbars for heights?
21961 tbh += this.toolbars[i].el.getHeight();
21962 //if (this.toolbars[i].footer) {
21963 // tbh += this.toolbars[i].footer.el.getHeight();
21971 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21972 ah -= 5; // knock a few pixes off for look..
21973 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21977 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21978 this.editorcore.onResize(ew,eh);
21983 * Toggles the editor between standard and source edit mode.
21984 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21986 toggleSourceEdit : function(sourceEditMode)
21988 this.editorcore.toggleSourceEdit(sourceEditMode);
21990 if(this.editorcore.sourceEditMode){
21991 Roo.log('editor - showing textarea');
21994 // Roo.log(this.syncValue());
21996 this.inputEl().removeClass(['hide', 'x-hidden']);
21997 this.inputEl().dom.removeAttribute('tabIndex');
21998 this.inputEl().focus();
22000 Roo.log('editor - hiding textarea');
22002 // Roo.log(this.pushValue());
22005 this.inputEl().addClass(['hide', 'x-hidden']);
22006 this.inputEl().dom.setAttribute('tabIndex', -1);
22007 //this.deferFocus();
22010 if(this.resizable){
22011 this.setSize(this.wrap.getSize());
22014 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22017 // private (for BoxComponent)
22018 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22020 // private (for BoxComponent)
22021 getResizeEl : function(){
22025 // private (for BoxComponent)
22026 getPositionEl : function(){
22031 initEvents : function(){
22032 this.originalValue = this.getValue();
22036 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22039 // markInvalid : Roo.emptyFn,
22041 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22044 // clearInvalid : Roo.emptyFn,
22046 setValue : function(v){
22047 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22048 this.editorcore.pushValue();
22053 deferFocus : function(){
22054 this.focus.defer(10, this);
22058 focus : function(){
22059 this.editorcore.focus();
22065 onDestroy : function(){
22071 for (var i =0; i < this.toolbars.length;i++) {
22072 // fixme - ask toolbars for heights?
22073 this.toolbars[i].onDestroy();
22076 this.wrap.dom.innerHTML = '';
22077 this.wrap.remove();
22082 onFirstFocus : function(){
22083 //Roo.log("onFirstFocus");
22084 this.editorcore.onFirstFocus();
22085 for (var i =0; i < this.toolbars.length;i++) {
22086 this.toolbars[i].onFirstFocus();
22092 syncValue : function()
22094 this.editorcore.syncValue();
22097 pushValue : function()
22099 this.editorcore.pushValue();
22103 // hide stuff that is not compatible
22117 * @event specialkey
22121 * @cfg {String} fieldClass @hide
22124 * @cfg {String} focusClass @hide
22127 * @cfg {String} autoCreate @hide
22130 * @cfg {String} inputType @hide
22133 * @cfg {String} invalidClass @hide
22136 * @cfg {String} invalidText @hide
22139 * @cfg {String} msgFx @hide
22142 * @cfg {String} validateOnBlur @hide
22151 Roo.namespace('Roo.bootstrap.htmleditor');
22153 * @class Roo.bootstrap.HtmlEditorToolbar1
22158 new Roo.bootstrap.HtmlEditor({
22161 new Roo.bootstrap.HtmlEditorToolbar1({
22162 disable : { fonts: 1 , format: 1, ..., ... , ...],
22168 * @cfg {Object} disable List of elements to disable..
22169 * @cfg {Array} btns List of additional buttons.
22173 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22176 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22179 Roo.apply(this, config);
22181 // default disabled, based on 'good practice'..
22182 this.disable = this.disable || {};
22183 Roo.applyIf(this.disable, {
22186 specialElements : true
22188 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22190 this.editor = config.editor;
22191 this.editorcore = config.editor.editorcore;
22193 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22195 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22196 // dont call parent... till later.
22198 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
22203 editorcore : false,
22208 "h1","h2","h3","h4","h5","h6",
22210 "abbr", "acronym", "address", "cite", "samp", "var",
22214 onRender : function(ct, position)
22216 // Roo.log("Call onRender: " + this.xtype);
22218 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22220 this.el.dom.style.marginBottom = '0';
22222 var editorcore = this.editorcore;
22223 var editor= this.editor;
22226 var btn = function(id,cmd , toggle, handler){
22228 var event = toggle ? 'toggle' : 'click';
22233 xns: Roo.bootstrap,
22236 enableToggle:toggle !== false,
22238 pressed : toggle ? false : null,
22241 a.listeners[toggle ? 'toggle' : 'click'] = function() {
22242 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
22251 xns: Roo.bootstrap,
22252 glyphicon : 'font',
22256 xns: Roo.bootstrap,
22260 Roo.each(this.formats, function(f) {
22261 style.menu.items.push({
22263 xns: Roo.bootstrap,
22264 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22269 editorcore.insertTag(this.tagname);
22276 children.push(style);
22279 btn('bold',false,true);
22280 btn('italic',false,true);
22281 btn('align-left', 'justifyleft',true);
22282 btn('align-center', 'justifycenter',true);
22283 btn('align-right' , 'justifyright',true);
22284 btn('link', false, false, function(btn) {
22285 //Roo.log("create link?");
22286 var url = prompt(this.createLinkText, this.defaultLinkValue);
22287 if(url && url != 'http:/'+'/'){
22288 this.editorcore.relayCmd('createlink', url);
22291 btn('list','insertunorderedlist',true);
22292 btn('pencil', false,true, function(btn){
22295 this.toggleSourceEdit(btn.pressed);
22301 xns: Roo.bootstrap,
22306 xns: Roo.bootstrap,
22311 cog.menu.items.push({
22313 xns: Roo.bootstrap,
22314 html : Clean styles,
22319 editorcore.insertTag(this.tagname);
22328 this.xtype = 'NavSimplebar';
22330 for(var i=0;i< children.length;i++) {
22332 this.buttons.add(this.addxtypeChild(children[i]));
22336 editor.on('editorevent', this.updateToolbar, this);
22338 onBtnClick : function(id)
22340 this.editorcore.relayCmd(id);
22341 this.editorcore.focus();
22345 * Protected method that will not generally be called directly. It triggers
22346 * a toolbar update by reading the markup state of the current selection in the editor.
22348 updateToolbar: function(){
22350 if(!this.editorcore.activated){
22351 this.editor.onFirstFocus(); // is this neeed?
22355 var btns = this.buttons;
22356 var doc = this.editorcore.doc;
22357 btns.get('bold').setActive(doc.queryCommandState('bold'));
22358 btns.get('italic').setActive(doc.queryCommandState('italic'));
22359 //btns.get('underline').setActive(doc.queryCommandState('underline'));
22361 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22362 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22363 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22365 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22366 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22369 var ans = this.editorcore.getAllAncestors();
22370 if (this.formatCombo) {
22373 var store = this.formatCombo.store;
22374 this.formatCombo.setValue("");
22375 for (var i =0; i < ans.length;i++) {
22376 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22378 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22386 // hides menus... - so this cant be on a menu...
22387 Roo.bootstrap.MenuMgr.hideAll();
22389 Roo.bootstrap.MenuMgr.hideAll();
22390 //this.editorsyncValue();
22392 onFirstFocus: function() {
22393 this.buttons.each(function(item){
22397 toggleSourceEdit : function(sourceEditMode){
22400 if(sourceEditMode){
22401 Roo.log("disabling buttons");
22402 this.buttons.each( function(item){
22403 if(item.cmd != 'pencil'){
22409 Roo.log("enabling buttons");
22410 if(this.editorcore.initialized){
22411 this.buttons.each( function(item){
22417 Roo.log("calling toggole on editor");
22418 // tell the editor that it's been pressed..
22419 this.editor.toggleSourceEdit(sourceEditMode);
22429 * @class Roo.bootstrap.Table.AbstractSelectionModel
22430 * @extends Roo.util.Observable
22431 * Abstract base class for grid SelectionModels. It provides the interface that should be
22432 * implemented by descendant classes. This class should not be directly instantiated.
22435 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22436 this.locked = false;
22437 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22441 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22442 /** @ignore Called by the grid automatically. Do not call directly. */
22443 init : function(grid){
22449 * Locks the selections.
22452 this.locked = true;
22456 * Unlocks the selections.
22458 unlock : function(){
22459 this.locked = false;
22463 * Returns true if the selections are locked.
22464 * @return {Boolean}
22466 isLocked : function(){
22467 return this.locked;
22471 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22472 * @class Roo.bootstrap.Table.RowSelectionModel
22473 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22474 * It supports multiple selections and keyboard selection/navigation.
22476 * @param {Object} config
22479 Roo.bootstrap.Table.RowSelectionModel = function(config){
22480 Roo.apply(this, config);
22481 this.selections = new Roo.util.MixedCollection(false, function(o){
22486 this.lastActive = false;
22490 * @event selectionchange
22491 * Fires when the selection changes
22492 * @param {SelectionModel} this
22494 "selectionchange" : true,
22496 * @event afterselectionchange
22497 * Fires after the selection changes (eg. by key press or clicking)
22498 * @param {SelectionModel} this
22500 "afterselectionchange" : true,
22502 * @event beforerowselect
22503 * Fires when a row is selected being selected, return false to cancel.
22504 * @param {SelectionModel} this
22505 * @param {Number} rowIndex The selected index
22506 * @param {Boolean} keepExisting False if other selections will be cleared
22508 "beforerowselect" : true,
22511 * Fires when a row is selected.
22512 * @param {SelectionModel} this
22513 * @param {Number} rowIndex The selected index
22514 * @param {Roo.data.Record} r The record
22516 "rowselect" : true,
22518 * @event rowdeselect
22519 * Fires when a row is deselected.
22520 * @param {SelectionModel} this
22521 * @param {Number} rowIndex The selected index
22523 "rowdeselect" : true
22525 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22526 this.locked = false;
22529 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22531 * @cfg {Boolean} singleSelect
22532 * True to allow selection of only one row at a time (defaults to false)
22534 singleSelect : false,
22537 initEvents : function()
22540 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22541 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
22542 //}else{ // allow click to work like normal
22543 // this.grid.on("rowclick", this.handleDragableRowClick, this);
22545 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22546 this.grid.on("rowclick", this.handleMouseDown, this);
22548 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22549 "up" : function(e){
22551 this.selectPrevious(e.shiftKey);
22552 }else if(this.last !== false && this.lastActive !== false){
22553 var last = this.last;
22554 this.selectRange(this.last, this.lastActive-1);
22555 this.grid.getView().focusRow(this.lastActive);
22556 if(last !== false){
22560 this.selectFirstRow();
22562 this.fireEvent("afterselectionchange", this);
22564 "down" : function(e){
22566 this.selectNext(e.shiftKey);
22567 }else if(this.last !== false && this.lastActive !== false){
22568 var last = this.last;
22569 this.selectRange(this.last, this.lastActive+1);
22570 this.grid.getView().focusRow(this.lastActive);
22571 if(last !== false){
22575 this.selectFirstRow();
22577 this.fireEvent("afterselectionchange", this);
22581 this.grid.store.on('load', function(){
22582 this.selections.clear();
22585 var view = this.grid.view;
22586 view.on("refresh", this.onRefresh, this);
22587 view.on("rowupdated", this.onRowUpdated, this);
22588 view.on("rowremoved", this.onRemove, this);
22593 onRefresh : function()
22595 var ds = this.grid.store, i, v = this.grid.view;
22596 var s = this.selections;
22597 s.each(function(r){
22598 if((i = ds.indexOfId(r.id)) != -1){
22607 onRemove : function(v, index, r){
22608 this.selections.remove(r);
22612 onRowUpdated : function(v, index, r){
22613 if(this.isSelected(r)){
22614 v.onRowSelect(index);
22620 * @param {Array} records The records to select
22621 * @param {Boolean} keepExisting (optional) True to keep existing selections
22623 selectRecords : function(records, keepExisting)
22626 this.clearSelections();
22628 var ds = this.grid.store;
22629 for(var i = 0, len = records.length; i < len; i++){
22630 this.selectRow(ds.indexOf(records[i]), true);
22635 * Gets the number of selected rows.
22638 getCount : function(){
22639 return this.selections.length;
22643 * Selects the first row in the grid.
22645 selectFirstRow : function(){
22650 * Select the last row.
22651 * @param {Boolean} keepExisting (optional) True to keep existing selections
22653 selectLastRow : function(keepExisting){
22654 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22655 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22659 * Selects the row immediately following the last selected row.
22660 * @param {Boolean} keepExisting (optional) True to keep existing selections
22662 selectNext : function(keepExisting)
22664 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22665 this.selectRow(this.last+1, keepExisting);
22666 this.grid.getView().focusRow(this.last);
22671 * Selects the row that precedes the last selected row.
22672 * @param {Boolean} keepExisting (optional) True to keep existing selections
22674 selectPrevious : function(keepExisting){
22676 this.selectRow(this.last-1, keepExisting);
22677 this.grid.getView().focusRow(this.last);
22682 * Returns the selected records
22683 * @return {Array} Array of selected records
22685 getSelections : function(){
22686 return [].concat(this.selections.items);
22690 * Returns the first selected record.
22693 getSelected : function(){
22694 return this.selections.itemAt(0);
22699 * Clears all selections.
22701 clearSelections : function(fast)
22707 var ds = this.grid.store;
22708 var s = this.selections;
22709 s.each(function(r){
22710 this.deselectRow(ds.indexOfId(r.id));
22714 this.selections.clear();
22721 * Selects all rows.
22723 selectAll : function(){
22727 this.selections.clear();
22728 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
22729 this.selectRow(i, true);
22734 * Returns True if there is a selection.
22735 * @return {Boolean}
22737 hasSelection : function(){
22738 return this.selections.length > 0;
22742 * Returns True if the specified row is selected.
22743 * @param {Number/Record} record The record or index of the record to check
22744 * @return {Boolean}
22746 isSelected : function(index){
22747 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
22748 return (r && this.selections.key(r.id) ? true : false);
22752 * Returns True if the specified record id is selected.
22753 * @param {String} id The id of record to check
22754 * @return {Boolean}
22756 isIdSelected : function(id){
22757 return (this.selections.key(id) ? true : false);
22762 handleMouseDBClick : function(e, t){
22766 handleMouseDown : function(e, t)
22768 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
22769 if(this.isLocked() || rowIndex < 0 ){
22772 if(e.shiftKey && this.last !== false){
22773 var last = this.last;
22774 this.selectRange(last, rowIndex, e.ctrlKey);
22775 this.last = last; // reset the last
22779 var isSelected = this.isSelected(rowIndex);
22780 //Roo.log("select row:" + rowIndex);
22782 this.deselectRow(rowIndex);
22784 this.selectRow(rowIndex, true);
22788 if(e.button !== 0 && isSelected){
22789 alert('rowIndex 2: ' + rowIndex);
22790 view.focusRow(rowIndex);
22791 }else if(e.ctrlKey && isSelected){
22792 this.deselectRow(rowIndex);
22793 }else if(!isSelected){
22794 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22795 view.focusRow(rowIndex);
22799 this.fireEvent("afterselectionchange", this);
22802 handleDragableRowClick : function(grid, rowIndex, e)
22804 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22805 this.selectRow(rowIndex, false);
22806 grid.view.focusRow(rowIndex);
22807 this.fireEvent("afterselectionchange", this);
22812 * Selects multiple rows.
22813 * @param {Array} rows Array of the indexes of the row to select
22814 * @param {Boolean} keepExisting (optional) True to keep existing selections
22816 selectRows : function(rows, keepExisting){
22818 this.clearSelections();
22820 for(var i = 0, len = rows.length; i < len; i++){
22821 this.selectRow(rows[i], true);
22826 * Selects a range of rows. All rows in between startRow and endRow are also selected.
22827 * @param {Number} startRow The index of the first row in the range
22828 * @param {Number} endRow The index of the last row in the range
22829 * @param {Boolean} keepExisting (optional) True to retain existing selections
22831 selectRange : function(startRow, endRow, keepExisting){
22836 this.clearSelections();
22838 if(startRow <= endRow){
22839 for(var i = startRow; i <= endRow; i++){
22840 this.selectRow(i, true);
22843 for(var i = startRow; i >= endRow; i--){
22844 this.selectRow(i, true);
22850 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22851 * @param {Number} startRow The index of the first row in the range
22852 * @param {Number} endRow The index of the last row in the range
22854 deselectRange : function(startRow, endRow, preventViewNotify){
22858 for(var i = startRow; i <= endRow; i++){
22859 this.deselectRow(i, preventViewNotify);
22865 * @param {Number} row The index of the row to select
22866 * @param {Boolean} keepExisting (optional) True to keep existing selections
22868 selectRow : function(index, keepExisting, preventViewNotify)
22870 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
22873 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22874 if(!keepExisting || this.singleSelect){
22875 this.clearSelections();
22878 var r = this.grid.store.getAt(index);
22879 //console.log('selectRow - record id :' + r.id);
22881 this.selections.add(r);
22882 this.last = this.lastActive = index;
22883 if(!preventViewNotify){
22884 var proxy = new Roo.Element(
22885 this.grid.getRowDom(index)
22887 proxy.addClass('bg-info info');
22889 this.fireEvent("rowselect", this, index, r);
22890 this.fireEvent("selectionchange", this);
22896 * @param {Number} row The index of the row to deselect
22898 deselectRow : function(index, preventViewNotify)
22903 if(this.last == index){
22906 if(this.lastActive == index){
22907 this.lastActive = false;
22910 var r = this.grid.store.getAt(index);
22915 this.selections.remove(r);
22916 //.console.log('deselectRow - record id :' + r.id);
22917 if(!preventViewNotify){
22919 var proxy = new Roo.Element(
22920 this.grid.getRowDom(index)
22922 proxy.removeClass('bg-info info');
22924 this.fireEvent("rowdeselect", this, index);
22925 this.fireEvent("selectionchange", this);
22929 restoreLast : function(){
22931 this.last = this._last;
22936 acceptsNav : function(row, col, cm){
22937 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22941 onEditorKey : function(field, e){
22942 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22947 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22949 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22951 }else if(k == e.ENTER && !e.ctrlKey){
22955 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22957 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22959 }else if(k == e.ESC){
22963 g.startEditing(newCell[0], newCell[1]);
22969 * Ext JS Library 1.1.1
22970 * Copyright(c) 2006-2007, Ext JS, LLC.
22972 * Originally Released Under LGPL - original licence link has changed is not relivant.
22975 * <script type="text/javascript">
22979 * @class Roo.bootstrap.PagingToolbar
22980 * @extends Roo.bootstrap.NavSimplebar
22981 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22983 * Create a new PagingToolbar
22984 * @param {Object} config The config object
22985 * @param {Roo.data.Store} store
22987 Roo.bootstrap.PagingToolbar = function(config)
22989 // old args format still supported... - xtype is prefered..
22990 // created from xtype...
22992 this.ds = config.dataSource;
22994 if (config.store && !this.ds) {
22995 this.store= Roo.factory(config.store, Roo.data);
22996 this.ds = this.store;
22997 this.ds.xmodule = this.xmodule || false;
23000 this.toolbarItems = [];
23001 if (config.items) {
23002 this.toolbarItems = config.items;
23005 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23010 this.bind(this.ds);
23013 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23017 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23019 * @cfg {Roo.data.Store} dataSource
23020 * The underlying data store providing the paged data
23023 * @cfg {String/HTMLElement/Element} container
23024 * container The id or element that will contain the toolbar
23027 * @cfg {Boolean} displayInfo
23028 * True to display the displayMsg (defaults to false)
23031 * @cfg {Number} pageSize
23032 * The number of records to display per page (defaults to 20)
23036 * @cfg {String} displayMsg
23037 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23039 displayMsg : 'Displaying {0} - {1} of {2}',
23041 * @cfg {String} emptyMsg
23042 * The message to display when no records are found (defaults to "No data to display")
23044 emptyMsg : 'No data to display',
23046 * Customizable piece of the default paging text (defaults to "Page")
23049 beforePageText : "Page",
23051 * Customizable piece of the default paging text (defaults to "of %0")
23054 afterPageText : "of {0}",
23056 * Customizable piece of the default paging text (defaults to "First Page")
23059 firstText : "First Page",
23061 * Customizable piece of the default paging text (defaults to "Previous Page")
23064 prevText : "Previous Page",
23066 * Customizable piece of the default paging text (defaults to "Next Page")
23069 nextText : "Next Page",
23071 * Customizable piece of the default paging text (defaults to "Last Page")
23074 lastText : "Last Page",
23076 * Customizable piece of the default paging text (defaults to "Refresh")
23079 refreshText : "Refresh",
23083 onRender : function(ct, position)
23085 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23086 this.navgroup.parentId = this.id;
23087 this.navgroup.onRender(this.el, null);
23088 // add the buttons to the navgroup
23090 if(this.displayInfo){
23091 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23092 this.displayEl = this.el.select('.x-paging-info', true).first();
23093 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23094 // this.displayEl = navel.el.select('span',true).first();
23100 Roo.each(_this.buttons, function(e){ // this might need to use render????
23101 Roo.factory(e).onRender(_this.el, null);
23105 Roo.each(_this.toolbarItems, function(e) {
23106 _this.navgroup.addItem(e);
23110 this.first = this.navgroup.addItem({
23111 tooltip: this.firstText,
23113 icon : 'fa fa-backward',
23115 preventDefault: true,
23116 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23119 this.prev = this.navgroup.addItem({
23120 tooltip: this.prevText,
23122 icon : 'fa fa-step-backward',
23124 preventDefault: true,
23125 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
23127 //this.addSeparator();
23130 var field = this.navgroup.addItem( {
23132 cls : 'x-paging-position',
23134 html : this.beforePageText +
23135 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23136 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
23139 this.field = field.el.select('input', true).first();
23140 this.field.on("keydown", this.onPagingKeydown, this);
23141 this.field.on("focus", function(){this.dom.select();});
23144 this.afterTextEl = field.el.select('.x-paging-after',true).first();
23145 //this.field.setHeight(18);
23146 //this.addSeparator();
23147 this.next = this.navgroup.addItem({
23148 tooltip: this.nextText,
23150 html : ' <i class="fa fa-step-forward">',
23152 preventDefault: true,
23153 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
23155 this.last = this.navgroup.addItem({
23156 tooltip: this.lastText,
23157 icon : 'fa fa-forward',
23160 preventDefault: true,
23161 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
23163 //this.addSeparator();
23164 this.loading = this.navgroup.addItem({
23165 tooltip: this.refreshText,
23166 icon: 'fa fa-refresh',
23167 preventDefault: true,
23168 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23174 updateInfo : function(){
23175 if(this.displayEl){
23176 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23177 var msg = count == 0 ?
23181 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
23183 this.displayEl.update(msg);
23188 onLoad : function(ds, r, o){
23189 this.cursor = o.params ? o.params.start : 0;
23190 var d = this.getPageData(),
23194 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23195 this.field.dom.value = ap;
23196 this.first.setDisabled(ap == 1);
23197 this.prev.setDisabled(ap == 1);
23198 this.next.setDisabled(ap == ps);
23199 this.last.setDisabled(ap == ps);
23200 this.loading.enable();
23205 getPageData : function(){
23206 var total = this.ds.getTotalCount();
23209 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23210 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23215 onLoadError : function(){
23216 this.loading.enable();
23220 onPagingKeydown : function(e){
23221 var k = e.getKey();
23222 var d = this.getPageData();
23224 var v = this.field.dom.value, pageNum;
23225 if(!v || isNaN(pageNum = parseInt(v, 10))){
23226 this.field.dom.value = d.activePage;
23229 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23230 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23233 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))
23235 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23236 this.field.dom.value = pageNum;
23237 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23240 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23242 var v = this.field.dom.value, pageNum;
23243 var increment = (e.shiftKey) ? 10 : 1;
23244 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23247 if(!v || isNaN(pageNum = parseInt(v, 10))) {
23248 this.field.dom.value = d.activePage;
23251 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23253 this.field.dom.value = parseInt(v, 10) + increment;
23254 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23255 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23262 beforeLoad : function(){
23264 this.loading.disable();
23269 onClick : function(which){
23278 ds.load({params:{start: 0, limit: this.pageSize}});
23281 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23284 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23287 var total = ds.getTotalCount();
23288 var extra = total % this.pageSize;
23289 var lastStart = extra ? (total - extra) : total-this.pageSize;
23290 ds.load({params:{start: lastStart, limit: this.pageSize}});
23293 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23299 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23300 * @param {Roo.data.Store} store The data store to unbind
23302 unbind : function(ds){
23303 ds.un("beforeload", this.beforeLoad, this);
23304 ds.un("load", this.onLoad, this);
23305 ds.un("loadexception", this.onLoadError, this);
23306 ds.un("remove", this.updateInfo, this);
23307 ds.un("add", this.updateInfo, this);
23308 this.ds = undefined;
23312 * Binds the paging toolbar to the specified {@link Roo.data.Store}
23313 * @param {Roo.data.Store} store The data store to bind
23315 bind : function(ds){
23316 ds.on("beforeload", this.beforeLoad, this);
23317 ds.on("load", this.onLoad, this);
23318 ds.on("loadexception", this.onLoadError, this);
23319 ds.on("remove", this.updateInfo, this);
23320 ds.on("add", this.updateInfo, this);
23331 * @class Roo.bootstrap.MessageBar
23332 * @extends Roo.bootstrap.Component
23333 * Bootstrap MessageBar class
23334 * @cfg {String} html contents of the MessageBar
23335 * @cfg {String} weight (info | success | warning | danger) default info
23336 * @cfg {String} beforeClass insert the bar before the given class
23337 * @cfg {Boolean} closable (true | false) default false
23338 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23341 * Create a new Element
23342 * @param {Object} config The config object
23345 Roo.bootstrap.MessageBar = function(config){
23346 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23349 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
23355 beforeClass: 'bootstrap-sticky-wrap',
23357 getAutoCreate : function(){
23361 cls: 'alert alert-dismissable alert-' + this.weight,
23366 html: this.html || ''
23372 cfg.cls += ' alert-messages-fixed';
23386 onRender : function(ct, position)
23388 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23391 var cfg = Roo.apply({}, this.getAutoCreate());
23395 cfg.cls += ' ' + this.cls;
23398 cfg.style = this.style;
23400 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23402 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23405 this.el.select('>button.close').on('click', this.hide, this);
23411 if (!this.rendered) {
23417 this.fireEvent('show', this);
23423 if (!this.rendered) {
23429 this.fireEvent('hide', this);
23432 update : function()
23434 // var e = this.el.dom.firstChild;
23436 // if(this.closable){
23437 // e = e.nextSibling;
23440 // e.data = this.html || '';
23442 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23458 * @class Roo.bootstrap.Graph
23459 * @extends Roo.bootstrap.Component
23460 * Bootstrap Graph class
23464 @cfg {String} graphtype bar | vbar | pie
23465 @cfg {number} g_x coodinator | centre x (pie)
23466 @cfg {number} g_y coodinator | centre y (pie)
23467 @cfg {number} g_r radius (pie)
23468 @cfg {number} g_height height of the chart (respected by all elements in the set)
23469 @cfg {number} g_width width of the chart (respected by all elements in the set)
23470 @cfg {Object} title The title of the chart
23473 -opts (object) options for the chart
23475 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23476 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23478 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.
23479 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23481 o stretch (boolean)
23483 -opts (object) options for the pie
23486 o startAngle (number)
23487 o endAngle (number)
23491 * Create a new Input
23492 * @param {Object} config The config object
23495 Roo.bootstrap.Graph = function(config){
23496 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23502 * The img click event for the img.
23503 * @param {Roo.EventObject} e
23509 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23520 //g_colors: this.colors,
23527 getAutoCreate : function(){
23538 onRender : function(ct,position){
23541 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23543 if (typeof(Raphael) == 'undefined') {
23544 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23548 this.raphael = Raphael(this.el.dom);
23550 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23551 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23552 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23553 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23555 r.text(160, 10, "Single Series Chart").attr(txtattr);
23556 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23557 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23558 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23560 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23561 r.barchart(330, 10, 300, 220, data1);
23562 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23563 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23566 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23567 // r.barchart(30, 30, 560, 250, xdata, {
23568 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23569 // axis : "0 0 1 1",
23570 // axisxlabels : xdata
23571 // //yvalues : cols,
23574 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23576 // this.load(null,xdata,{
23577 // axis : "0 0 1 1",
23578 // axisxlabels : xdata
23583 load : function(graphtype,xdata,opts)
23585 this.raphael.clear();
23587 graphtype = this.graphtype;
23592 var r = this.raphael,
23593 fin = function () {
23594 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23596 fout = function () {
23597 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23599 pfin = function() {
23600 this.sector.stop();
23601 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23604 this.label[0].stop();
23605 this.label[0].attr({ r: 7.5 });
23606 this.label[1].attr({ "font-weight": 800 });
23609 pfout = function() {
23610 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23613 this.label[0].animate({ r: 5 }, 500, "bounce");
23614 this.label[1].attr({ "font-weight": 400 });
23620 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23623 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23626 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23627 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23629 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23636 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23641 setTitle: function(o)
23646 initEvents: function() {
23649 this.el.on('click', this.onClick, this);
23653 onClick : function(e)
23655 Roo.log('img onclick');
23656 this.fireEvent('click', this, e);
23668 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23671 * @class Roo.bootstrap.dash.NumberBox
23672 * @extends Roo.bootstrap.Component
23673 * Bootstrap NumberBox class
23674 * @cfg {String} headline Box headline
23675 * @cfg {String} content Box content
23676 * @cfg {String} icon Box icon
23677 * @cfg {String} footer Footer text
23678 * @cfg {String} fhref Footer href
23681 * Create a new NumberBox
23682 * @param {Object} config The config object
23686 Roo.bootstrap.dash.NumberBox = function(config){
23687 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23691 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23700 getAutoCreate : function(){
23704 cls : 'small-box ',
23712 cls : 'roo-headline',
23713 html : this.headline
23717 cls : 'roo-content',
23718 html : this.content
23732 cls : 'ion ' + this.icon
23741 cls : 'small-box-footer',
23742 href : this.fhref || '#',
23746 cfg.cn.push(footer);
23753 onRender : function(ct,position){
23754 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23761 setHeadline: function (value)
23763 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23766 setFooter: function (value, href)
23768 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23771 this.el.select('a.small-box-footer',true).first().attr('href', href);
23776 setContent: function (value)
23778 this.el.select('.roo-content',true).first().dom.innerHTML = value;
23781 initEvents: function()
23795 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23798 * @class Roo.bootstrap.dash.TabBox
23799 * @extends Roo.bootstrap.Component
23800 * Bootstrap TabBox class
23801 * @cfg {String} title Title of the TabBox
23802 * @cfg {String} icon Icon of the TabBox
23803 * @cfg {Boolean} showtabs (true|false) show the tabs default true
23804 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23807 * Create a new TabBox
23808 * @param {Object} config The config object
23812 Roo.bootstrap.dash.TabBox = function(config){
23813 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23818 * When a pane is added
23819 * @param {Roo.bootstrap.dash.TabPane} pane
23823 * @event activatepane
23824 * When a pane is activated
23825 * @param {Roo.bootstrap.dash.TabPane} pane
23827 "activatepane" : true
23835 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
23840 tabScrollable : false,
23842 getChildContainer : function()
23844 return this.el.select('.tab-content', true).first();
23847 getAutoCreate : function(){
23851 cls: 'pull-left header',
23859 cls: 'fa ' + this.icon
23865 cls: 'nav nav-tabs pull-right',
23871 if(this.tabScrollable){
23878 cls: 'nav nav-tabs pull-right',
23889 cls: 'nav-tabs-custom',
23894 cls: 'tab-content no-padding',
23902 initEvents : function()
23904 //Roo.log('add add pane handler');
23905 this.on('addpane', this.onAddPane, this);
23908 * Updates the box title
23909 * @param {String} html to set the title to.
23911 setTitle : function(value)
23913 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23915 onAddPane : function(pane)
23917 this.panes.push(pane);
23918 //Roo.log('addpane');
23920 // tabs are rendere left to right..
23921 if(!this.showtabs){
23925 var ctr = this.el.select('.nav-tabs', true).first();
23928 var existing = ctr.select('.nav-tab',true);
23929 var qty = existing.getCount();;
23932 var tab = ctr.createChild({
23934 cls : 'nav-tab' + (qty ? '' : ' active'),
23942 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23945 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23947 pane.el.addClass('active');
23952 onTabClick : function(ev,un,ob,pane)
23954 //Roo.log('tab - prev default');
23955 ev.preventDefault();
23958 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23959 pane.tab.addClass('active');
23960 //Roo.log(pane.title);
23961 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23962 // technically we should have a deactivate event.. but maybe add later.
23963 // and it should not de-activate the selected tab...
23964 this.fireEvent('activatepane', pane);
23965 pane.el.addClass('active');
23966 pane.fireEvent('activate');
23971 getActivePane : function()
23974 Roo.each(this.panes, function(p) {
23975 if(p.el.hasClass('active')){
23996 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23998 * @class Roo.bootstrap.TabPane
23999 * @extends Roo.bootstrap.Component
24000 * Bootstrap TabPane class
24001 * @cfg {Boolean} active (false | true) Default false
24002 * @cfg {String} title title of panel
24006 * Create a new TabPane
24007 * @param {Object} config The config object
24010 Roo.bootstrap.dash.TabPane = function(config){
24011 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24017 * When a pane is activated
24018 * @param {Roo.bootstrap.dash.TabPane} pane
24025 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
24030 // the tabBox that this is attached to.
24033 getAutoCreate : function()
24041 cfg.cls += ' active';
24046 initEvents : function()
24048 //Roo.log('trigger add pane handler');
24049 this.parent().fireEvent('addpane', this)
24053 * Updates the tab title
24054 * @param {String} html to set the title to.
24056 setTitle: function(str)
24062 this.tab.select('a', true).first().dom.innerHTML = str;
24079 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24082 * @class Roo.bootstrap.menu.Menu
24083 * @extends Roo.bootstrap.Component
24084 * Bootstrap Menu class - container for Menu
24085 * @cfg {String} html Text of the menu
24086 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24087 * @cfg {String} icon Font awesome icon
24088 * @cfg {String} pos Menu align to (top | bottom) default bottom
24092 * Create a new Menu
24093 * @param {Object} config The config object
24097 Roo.bootstrap.menu.Menu = function(config){
24098 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24102 * @event beforeshow
24103 * Fires before this menu is displayed
24104 * @param {Roo.bootstrap.menu.Menu} this
24108 * @event beforehide
24109 * Fires before this menu is hidden
24110 * @param {Roo.bootstrap.menu.Menu} this
24115 * Fires after this menu is displayed
24116 * @param {Roo.bootstrap.menu.Menu} this
24121 * Fires after this menu is hidden
24122 * @param {Roo.bootstrap.menu.Menu} this
24127 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24128 * @param {Roo.bootstrap.menu.Menu} this
24129 * @param {Roo.EventObject} e
24136 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
24140 weight : 'default',
24145 getChildContainer : function() {
24146 if(this.isSubMenu){
24150 return this.el.select('ul.dropdown-menu', true).first();
24153 getAutoCreate : function()
24158 cls : 'roo-menu-text',
24166 cls : 'fa ' + this.icon
24177 cls : 'dropdown-button btn btn-' + this.weight,
24182 cls : 'dropdown-toggle btn btn-' + this.weight,
24192 cls : 'dropdown-menu'
24198 if(this.pos == 'top'){
24199 cfg.cls += ' dropup';
24202 if(this.isSubMenu){
24205 cls : 'dropdown-menu'
24212 onRender : function(ct, position)
24214 this.isSubMenu = ct.hasClass('dropdown-submenu');
24216 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24219 initEvents : function()
24221 if(this.isSubMenu){
24225 this.hidden = true;
24227 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24228 this.triggerEl.on('click', this.onTriggerPress, this);
24230 this.buttonEl = this.el.select('button.dropdown-button', true).first();
24231 this.buttonEl.on('click', this.onClick, this);
24237 if(this.isSubMenu){
24241 return this.el.select('ul.dropdown-menu', true).first();
24244 onClick : function(e)
24246 this.fireEvent("click", this, e);
24249 onTriggerPress : function(e)
24251 if (this.isVisible()) {
24258 isVisible : function(){
24259 return !this.hidden;
24264 this.fireEvent("beforeshow", this);
24266 this.hidden = false;
24267 this.el.addClass('open');
24269 Roo.get(document).on("mouseup", this.onMouseUp, this);
24271 this.fireEvent("show", this);
24278 this.fireEvent("beforehide", this);
24280 this.hidden = true;
24281 this.el.removeClass('open');
24283 Roo.get(document).un("mouseup", this.onMouseUp);
24285 this.fireEvent("hide", this);
24288 onMouseUp : function()
24302 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24305 * @class Roo.bootstrap.menu.Item
24306 * @extends Roo.bootstrap.Component
24307 * Bootstrap MenuItem class
24308 * @cfg {Boolean} submenu (true | false) default false
24309 * @cfg {String} html text of the item
24310 * @cfg {String} href the link
24311 * @cfg {Boolean} disable (true | false) default false
24312 * @cfg {Boolean} preventDefault (true | false) default true
24313 * @cfg {String} icon Font awesome icon
24314 * @cfg {String} pos Submenu align to (left | right) default right
24318 * Create a new Item
24319 * @param {Object} config The config object
24323 Roo.bootstrap.menu.Item = function(config){
24324 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24328 * Fires when the mouse is hovering over this menu
24329 * @param {Roo.bootstrap.menu.Item} this
24330 * @param {Roo.EventObject} e
24335 * Fires when the mouse exits this menu
24336 * @param {Roo.bootstrap.menu.Item} this
24337 * @param {Roo.EventObject} e
24343 * The raw click event for the entire grid.
24344 * @param {Roo.EventObject} e
24350 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
24355 preventDefault: true,
24360 getAutoCreate : function()
24365 cls : 'roo-menu-item-text',
24373 cls : 'fa ' + this.icon
24382 href : this.href || '#',
24389 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24393 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24395 if(this.pos == 'left'){
24396 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24403 initEvents : function()
24405 this.el.on('mouseover', this.onMouseOver, this);
24406 this.el.on('mouseout', this.onMouseOut, this);
24408 this.el.select('a', true).first().on('click', this.onClick, this);
24412 onClick : function(e)
24414 if(this.preventDefault){
24415 e.preventDefault();
24418 this.fireEvent("click", this, e);
24421 onMouseOver : function(e)
24423 if(this.submenu && this.pos == 'left'){
24424 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24427 this.fireEvent("mouseover", this, e);
24430 onMouseOut : function(e)
24432 this.fireEvent("mouseout", this, e);
24444 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24447 * @class Roo.bootstrap.menu.Separator
24448 * @extends Roo.bootstrap.Component
24449 * Bootstrap Separator class
24452 * Create a new Separator
24453 * @param {Object} config The config object
24457 Roo.bootstrap.menu.Separator = function(config){
24458 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24461 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24463 getAutoCreate : function(){
24484 * @class Roo.bootstrap.Tooltip
24485 * Bootstrap Tooltip class
24486 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24487 * to determine which dom element triggers the tooltip.
24489 * It needs to add support for additional attributes like tooltip-position
24492 * Create a new Toolti
24493 * @param {Object} config The config object
24496 Roo.bootstrap.Tooltip = function(config){
24497 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24500 Roo.apply(Roo.bootstrap.Tooltip, {
24502 * @function init initialize tooltip monitoring.
24506 currentTip : false,
24507 currentRegion : false,
24513 Roo.get(document).on('mouseover', this.enter ,this);
24514 Roo.get(document).on('mouseout', this.leave, this);
24517 this.currentTip = new Roo.bootstrap.Tooltip();
24520 enter : function(ev)
24522 var dom = ev.getTarget();
24524 //Roo.log(['enter',dom]);
24525 var el = Roo.fly(dom);
24526 if (this.currentEl) {
24528 //Roo.log(this.currentEl);
24529 //Roo.log(this.currentEl.contains(dom));
24530 if (this.currentEl == el) {
24533 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24539 if (this.currentTip.el) {
24540 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24544 if(!el || el.dom == document){
24550 // you can not look for children, as if el is the body.. then everythign is the child..
24551 if (!el.attr('tooltip')) { //
24552 if (!el.select("[tooltip]").elements.length) {
24555 // is the mouse over this child...?
24556 bindEl = el.select("[tooltip]").first();
24557 var xy = ev.getXY();
24558 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24559 //Roo.log("not in region.");
24562 //Roo.log("child element over..");
24565 this.currentEl = bindEl;
24566 this.currentTip.bind(bindEl);
24567 this.currentRegion = Roo.lib.Region.getRegion(dom);
24568 this.currentTip.enter();
24571 leave : function(ev)
24573 var dom = ev.getTarget();
24574 //Roo.log(['leave',dom]);
24575 if (!this.currentEl) {
24580 if (dom != this.currentEl.dom) {
24583 var xy = ev.getXY();
24584 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24587 // only activate leave if mouse cursor is outside... bounding box..
24592 if (this.currentTip) {
24593 this.currentTip.leave();
24595 //Roo.log('clear currentEl');
24596 this.currentEl = false;
24601 'left' : ['r-l', [-2,0], 'right'],
24602 'right' : ['l-r', [2,0], 'left'],
24603 'bottom' : ['t-b', [0,2], 'top'],
24604 'top' : [ 'b-t', [0,-2], 'bottom']
24610 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24615 delay : null, // can be { show : 300 , hide: 500}
24619 hoverState : null, //???
24621 placement : 'bottom',
24623 getAutoCreate : function(){
24630 cls : 'tooltip-arrow'
24633 cls : 'tooltip-inner'
24640 bind : function(el)
24646 enter : function () {
24648 if (this.timeout != null) {
24649 clearTimeout(this.timeout);
24652 this.hoverState = 'in';
24653 //Roo.log("enter - show");
24654 if (!this.delay || !this.delay.show) {
24659 this.timeout = setTimeout(function () {
24660 if (_t.hoverState == 'in') {
24663 }, this.delay.show);
24667 clearTimeout(this.timeout);
24669 this.hoverState = 'out';
24670 if (!this.delay || !this.delay.hide) {
24676 this.timeout = setTimeout(function () {
24677 //Roo.log("leave - timeout");
24679 if (_t.hoverState == 'out') {
24681 Roo.bootstrap.Tooltip.currentEl = false;
24689 this.render(document.body);
24692 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24694 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24696 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24698 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24700 var placement = typeof this.placement == 'function' ?
24701 this.placement.call(this, this.el, on_el) :
24704 var autoToken = /\s?auto?\s?/i;
24705 var autoPlace = autoToken.test(placement);
24707 placement = placement.replace(autoToken, '') || 'top';
24711 //this.el.setXY([0,0]);
24713 //this.el.dom.style.display='block';
24715 //this.el.appendTo(on_el);
24717 var p = this.getPosition();
24718 var box = this.el.getBox();
24724 var align = Roo.bootstrap.Tooltip.alignment[placement];
24726 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24728 if(placement == 'top' || placement == 'bottom'){
24730 placement = 'right';
24733 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24734 placement = 'left';
24737 var scroll = Roo.select('body', true).first().getScroll();
24739 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24745 align = Roo.bootstrap.Tooltip.alignment[placement];
24747 this.el.alignTo(this.bindEl, align[0],align[1]);
24748 //var arrow = this.el.select('.arrow',true).first();
24749 //arrow.set(align[2],
24751 this.el.addClass(placement);
24753 this.el.addClass('in fade');
24755 this.hoverState = null;
24757 if (this.el.hasClass('fade')) {
24768 //this.el.setXY([0,0]);
24769 this.el.removeClass('in');
24785 * @class Roo.bootstrap.LocationPicker
24786 * @extends Roo.bootstrap.Component
24787 * Bootstrap LocationPicker class
24788 * @cfg {Number} latitude Position when init default 0
24789 * @cfg {Number} longitude Position when init default 0
24790 * @cfg {Number} zoom default 15
24791 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24792 * @cfg {Boolean} mapTypeControl default false
24793 * @cfg {Boolean} disableDoubleClickZoom default false
24794 * @cfg {Boolean} scrollwheel default true
24795 * @cfg {Boolean} streetViewControl default false
24796 * @cfg {Number} radius default 0
24797 * @cfg {String} locationName
24798 * @cfg {Boolean} draggable default true
24799 * @cfg {Boolean} enableAutocomplete default false
24800 * @cfg {Boolean} enableReverseGeocode default true
24801 * @cfg {String} markerTitle
24804 * Create a new LocationPicker
24805 * @param {Object} config The config object
24809 Roo.bootstrap.LocationPicker = function(config){
24811 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24816 * Fires when the picker initialized.
24817 * @param {Roo.bootstrap.LocationPicker} this
24818 * @param {Google Location} location
24822 * @event positionchanged
24823 * Fires when the picker position changed.
24824 * @param {Roo.bootstrap.LocationPicker} this
24825 * @param {Google Location} location
24827 positionchanged : true,
24830 * Fires when the map resize.
24831 * @param {Roo.bootstrap.LocationPicker} this
24836 * Fires when the map show.
24837 * @param {Roo.bootstrap.LocationPicker} this
24842 * Fires when the map hide.
24843 * @param {Roo.bootstrap.LocationPicker} this
24848 * Fires when click the map.
24849 * @param {Roo.bootstrap.LocationPicker} this
24850 * @param {Map event} e
24854 * @event mapRightClick
24855 * Fires when right click the map.
24856 * @param {Roo.bootstrap.LocationPicker} this
24857 * @param {Map event} e
24859 mapRightClick : true,
24861 * @event markerClick
24862 * Fires when click the marker.
24863 * @param {Roo.bootstrap.LocationPicker} this
24864 * @param {Map event} e
24866 markerClick : true,
24868 * @event markerRightClick
24869 * Fires when right click the marker.
24870 * @param {Roo.bootstrap.LocationPicker} this
24871 * @param {Map event} e
24873 markerRightClick : true,
24875 * @event OverlayViewDraw
24876 * Fires when OverlayView Draw
24877 * @param {Roo.bootstrap.LocationPicker} this
24879 OverlayViewDraw : true,
24881 * @event OverlayViewOnAdd
24882 * Fires when OverlayView Draw
24883 * @param {Roo.bootstrap.LocationPicker} this
24885 OverlayViewOnAdd : true,
24887 * @event OverlayViewOnRemove
24888 * Fires when OverlayView Draw
24889 * @param {Roo.bootstrap.LocationPicker} this
24891 OverlayViewOnRemove : true,
24893 * @event OverlayViewShow
24894 * Fires when OverlayView Draw
24895 * @param {Roo.bootstrap.LocationPicker} this
24896 * @param {Pixel} cpx
24898 OverlayViewShow : true,
24900 * @event OverlayViewHide
24901 * Fires when OverlayView Draw
24902 * @param {Roo.bootstrap.LocationPicker} this
24904 OverlayViewHide : true,
24906 * @event loadexception
24907 * Fires when load google lib failed.
24908 * @param {Roo.bootstrap.LocationPicker} this
24910 loadexception : true
24915 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24917 gMapContext: false,
24923 mapTypeControl: false,
24924 disableDoubleClickZoom: false,
24926 streetViewControl: false,
24930 enableAutocomplete: false,
24931 enableReverseGeocode: true,
24934 getAutoCreate: function()
24939 cls: 'roo-location-picker'
24945 initEvents: function(ct, position)
24947 if(!this.el.getWidth() || this.isApplied()){
24951 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24956 initial: function()
24958 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24959 this.fireEvent('loadexception', this);
24963 if(!this.mapTypeId){
24964 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24967 this.gMapContext = this.GMapContext();
24969 this.initOverlayView();
24971 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24975 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24976 _this.setPosition(_this.gMapContext.marker.position);
24979 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24980 _this.fireEvent('mapClick', this, event);
24984 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24985 _this.fireEvent('mapRightClick', this, event);
24989 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24990 _this.fireEvent('markerClick', this, event);
24994 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24995 _this.fireEvent('markerRightClick', this, event);
24999 this.setPosition(this.gMapContext.location);
25001 this.fireEvent('initial', this, this.gMapContext.location);
25004 initOverlayView: function()
25008 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25012 _this.fireEvent('OverlayViewDraw', _this);
25017 _this.fireEvent('OverlayViewOnAdd', _this);
25020 onRemove: function()
25022 _this.fireEvent('OverlayViewOnRemove', _this);
25025 show: function(cpx)
25027 _this.fireEvent('OverlayViewShow', _this, cpx);
25032 _this.fireEvent('OverlayViewHide', _this);
25038 fromLatLngToContainerPixel: function(event)
25040 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25043 isApplied: function()
25045 return this.getGmapContext() == false ? false : true;
25048 getGmapContext: function()
25050 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25053 GMapContext: function()
25055 var position = new google.maps.LatLng(this.latitude, this.longitude);
25057 var _map = new google.maps.Map(this.el.dom, {
25060 mapTypeId: this.mapTypeId,
25061 mapTypeControl: this.mapTypeControl,
25062 disableDoubleClickZoom: this.disableDoubleClickZoom,
25063 scrollwheel: this.scrollwheel,
25064 streetViewControl: this.streetViewControl,
25065 locationName: this.locationName,
25066 draggable: this.draggable,
25067 enableAutocomplete: this.enableAutocomplete,
25068 enableReverseGeocode: this.enableReverseGeocode
25071 var _marker = new google.maps.Marker({
25072 position: position,
25074 title: this.markerTitle,
25075 draggable: this.draggable
25082 location: position,
25083 radius: this.radius,
25084 locationName: this.locationName,
25085 addressComponents: {
25086 formatted_address: null,
25087 addressLine1: null,
25088 addressLine2: null,
25090 streetNumber: null,
25094 stateOrProvince: null
25097 domContainer: this.el.dom,
25098 geodecoder: new google.maps.Geocoder()
25102 drawCircle: function(center, radius, options)
25104 if (this.gMapContext.circle != null) {
25105 this.gMapContext.circle.setMap(null);
25109 options = Roo.apply({}, options, {
25110 strokeColor: "#0000FF",
25111 strokeOpacity: .35,
25113 fillColor: "#0000FF",
25117 options.map = this.gMapContext.map;
25118 options.radius = radius;
25119 options.center = center;
25120 this.gMapContext.circle = new google.maps.Circle(options);
25121 return this.gMapContext.circle;
25127 setPosition: function(location)
25129 this.gMapContext.location = location;
25130 this.gMapContext.marker.setPosition(location);
25131 this.gMapContext.map.panTo(location);
25132 this.drawCircle(location, this.gMapContext.radius, {});
25136 if (this.gMapContext.settings.enableReverseGeocode) {
25137 this.gMapContext.geodecoder.geocode({
25138 latLng: this.gMapContext.location
25139 }, function(results, status) {
25141 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25142 _this.gMapContext.locationName = results[0].formatted_address;
25143 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25145 _this.fireEvent('positionchanged', this, location);
25152 this.fireEvent('positionchanged', this, location);
25157 google.maps.event.trigger(this.gMapContext.map, "resize");
25159 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25161 this.fireEvent('resize', this);
25164 setPositionByLatLng: function(latitude, longitude)
25166 this.setPosition(new google.maps.LatLng(latitude, longitude));
25169 getCurrentPosition: function()
25172 latitude: this.gMapContext.location.lat(),
25173 longitude: this.gMapContext.location.lng()
25177 getAddressName: function()
25179 return this.gMapContext.locationName;
25182 getAddressComponents: function()
25184 return this.gMapContext.addressComponents;
25187 address_component_from_google_geocode: function(address_components)
25191 for (var i = 0; i < address_components.length; i++) {
25192 var component = address_components[i];
25193 if (component.types.indexOf("postal_code") >= 0) {
25194 result.postalCode = component.short_name;
25195 } else if (component.types.indexOf("street_number") >= 0) {
25196 result.streetNumber = component.short_name;
25197 } else if (component.types.indexOf("route") >= 0) {
25198 result.streetName = component.short_name;
25199 } else if (component.types.indexOf("neighborhood") >= 0) {
25200 result.city = component.short_name;
25201 } else if (component.types.indexOf("locality") >= 0) {
25202 result.city = component.short_name;
25203 } else if (component.types.indexOf("sublocality") >= 0) {
25204 result.district = component.short_name;
25205 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25206 result.stateOrProvince = component.short_name;
25207 } else if (component.types.indexOf("country") >= 0) {
25208 result.country = component.short_name;
25212 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25213 result.addressLine2 = "";
25217 setZoomLevel: function(zoom)
25219 this.gMapContext.map.setZoom(zoom);
25232 this.fireEvent('show', this);
25243 this.fireEvent('hide', this);
25248 Roo.apply(Roo.bootstrap.LocationPicker, {
25250 OverlayView : function(map, options)
25252 options = options || {};
25266 * @class Roo.bootstrap.Alert
25267 * @extends Roo.bootstrap.Component
25268 * Bootstrap Alert class
25269 * @cfg {String} title The title of alert
25270 * @cfg {String} html The content of alert
25271 * @cfg {String} weight ( success | info | warning | danger )
25272 * @cfg {String} faicon font-awesomeicon
25275 * Create a new alert
25276 * @param {Object} config The config object
25280 Roo.bootstrap.Alert = function(config){
25281 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25285 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
25292 getAutoCreate : function()
25301 cls : 'roo-alert-icon'
25306 cls : 'roo-alert-title',
25311 cls : 'roo-alert-text',
25318 cfg.cn[0].cls += ' fa ' + this.faicon;
25322 cfg.cls += ' alert-' + this.weight;
25328 initEvents: function()
25330 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25333 setTitle : function(str)
25335 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25338 setText : function(str)
25340 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25343 setWeight : function(weight)
25346 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25349 this.weight = weight;
25351 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25354 setIcon : function(icon)
25357 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25360 this.faicon = icon;
25362 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25383 * @class Roo.bootstrap.UploadCropbox
25384 * @extends Roo.bootstrap.Component
25385 * Bootstrap UploadCropbox class
25386 * @cfg {String} emptyText show when image has been loaded
25387 * @cfg {String} rotateNotify show when image too small to rotate
25388 * @cfg {Number} errorTimeout default 3000
25389 * @cfg {Number} minWidth default 300
25390 * @cfg {Number} minHeight default 300
25391 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25392 * @cfg {Boolean} isDocument (true|false) default false
25393 * @cfg {String} url action url
25394 * @cfg {String} paramName default 'imageUpload'
25395 * @cfg {String} method default POST
25396 * @cfg {Boolean} loadMask (true|false) default true
25397 * @cfg {Boolean} loadingText default 'Loading...'
25400 * Create a new UploadCropbox
25401 * @param {Object} config The config object
25404 Roo.bootstrap.UploadCropbox = function(config){
25405 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25409 * @event beforeselectfile
25410 * Fire before select file
25411 * @param {Roo.bootstrap.UploadCropbox} this
25413 "beforeselectfile" : true,
25416 * Fire after initEvent
25417 * @param {Roo.bootstrap.UploadCropbox} this
25422 * Fire after initEvent
25423 * @param {Roo.bootstrap.UploadCropbox} this
25424 * @param {String} data
25429 * Fire when preparing the file data
25430 * @param {Roo.bootstrap.UploadCropbox} this
25431 * @param {Object} file
25436 * Fire when get exception
25437 * @param {Roo.bootstrap.UploadCropbox} this
25438 * @param {XMLHttpRequest} xhr
25440 "exception" : true,
25442 * @event beforeloadcanvas
25443 * Fire before load the canvas
25444 * @param {Roo.bootstrap.UploadCropbox} this
25445 * @param {String} src
25447 "beforeloadcanvas" : true,
25450 * Fire when trash image
25451 * @param {Roo.bootstrap.UploadCropbox} this
25456 * Fire when download the image
25457 * @param {Roo.bootstrap.UploadCropbox} this
25461 * @event footerbuttonclick
25462 * Fire when footerbuttonclick
25463 * @param {Roo.bootstrap.UploadCropbox} this
25464 * @param {String} type
25466 "footerbuttonclick" : true,
25470 * @param {Roo.bootstrap.UploadCropbox} this
25475 * Fire when rotate the image
25476 * @param {Roo.bootstrap.UploadCropbox} this
25477 * @param {String} pos
25482 * Fire when inspect the file
25483 * @param {Roo.bootstrap.UploadCropbox} this
25484 * @param {Object} file
25489 * Fire when xhr upload the file
25490 * @param {Roo.bootstrap.UploadCropbox} this
25491 * @param {Object} data
25496 * Fire when arrange the file data
25497 * @param {Roo.bootstrap.UploadCropbox} this
25498 * @param {Object} formData
25503 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25506 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25508 emptyText : 'Click to upload image',
25509 rotateNotify : 'Image is too small to rotate',
25510 errorTimeout : 3000,
25524 cropType : 'image/jpeg',
25526 canvasLoaded : false,
25527 isDocument : false,
25529 paramName : 'imageUpload',
25531 loadingText : 'Loading...',
25534 getAutoCreate : function()
25538 cls : 'roo-upload-cropbox',
25542 cls : 'roo-upload-cropbox-selector',
25547 cls : 'roo-upload-cropbox-body',
25548 style : 'cursor:pointer',
25552 cls : 'roo-upload-cropbox-preview'
25556 cls : 'roo-upload-cropbox-thumb'
25560 cls : 'roo-upload-cropbox-empty-notify',
25561 html : this.emptyText
25565 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25566 html : this.rotateNotify
25572 cls : 'roo-upload-cropbox-footer',
25575 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25585 onRender : function(ct, position)
25587 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25589 if (this.buttons.length) {
25591 Roo.each(this.buttons, function(bb) {
25593 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25595 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25601 this.maskEl = this.el;
25605 initEvents : function()
25607 this.urlAPI = (window.createObjectURL && window) ||
25608 (window.URL && URL.revokeObjectURL && URL) ||
25609 (window.webkitURL && webkitURL);
25611 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25612 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25614 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25615 this.selectorEl.hide();
25617 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25618 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25620 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25621 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25622 this.thumbEl.hide();
25624 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25625 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25627 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25628 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25629 this.errorEl.hide();
25631 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25632 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25633 this.footerEl.hide();
25635 this.setThumbBoxSize();
25641 this.fireEvent('initial', this);
25648 window.addEventListener("resize", function() { _this.resize(); } );
25650 this.bodyEl.on('click', this.beforeSelectFile, this);
25653 this.bodyEl.on('touchstart', this.onTouchStart, this);
25654 this.bodyEl.on('touchmove', this.onTouchMove, this);
25655 this.bodyEl.on('touchend', this.onTouchEnd, this);
25659 this.bodyEl.on('mousedown', this.onMouseDown, this);
25660 this.bodyEl.on('mousemove', this.onMouseMove, this);
25661 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25662 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25663 Roo.get(document).on('mouseup', this.onMouseUp, this);
25666 this.selectorEl.on('change', this.onFileSelected, this);
25672 this.baseScale = 1;
25674 this.baseRotate = 1;
25675 this.dragable = false;
25676 this.pinching = false;
25679 this.cropData = false;
25680 this.notifyEl.dom.innerHTML = this.emptyText;
25682 this.selectorEl.dom.value = '';
25686 resize : function()
25688 if(this.fireEvent('resize', this) != false){
25689 this.setThumbBoxPosition();
25690 this.setCanvasPosition();
25694 onFooterButtonClick : function(e, el, o, type)
25697 case 'rotate-left' :
25698 this.onRotateLeft(e);
25700 case 'rotate-right' :
25701 this.onRotateRight(e);
25704 this.beforeSelectFile(e);
25719 this.fireEvent('footerbuttonclick', this, type);
25722 beforeSelectFile : function(e)
25724 e.preventDefault();
25726 if(this.fireEvent('beforeselectfile', this) != false){
25727 this.selectorEl.dom.click();
25731 onFileSelected : function(e)
25733 e.preventDefault();
25735 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25739 var file = this.selectorEl.dom.files[0];
25741 if(this.fireEvent('inspect', this, file) != false){
25742 this.prepare(file);
25747 trash : function(e)
25749 this.fireEvent('trash', this);
25752 download : function(e)
25754 this.fireEvent('download', this);
25757 loadCanvas : function(src)
25759 if(this.fireEvent('beforeloadcanvas', this, src) != false){
25763 this.imageEl = document.createElement('img');
25767 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25769 this.imageEl.src = src;
25773 onLoadCanvas : function()
25775 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25776 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25778 this.bodyEl.un('click', this.beforeSelectFile, this);
25780 this.notifyEl.hide();
25781 this.thumbEl.show();
25782 this.footerEl.show();
25784 this.baseRotateLevel();
25786 if(this.isDocument){
25787 this.setThumbBoxSize();
25790 this.setThumbBoxPosition();
25792 this.baseScaleLevel();
25798 this.canvasLoaded = true;
25801 this.maskEl.unmask();
25806 setCanvasPosition : function()
25808 if(!this.canvasEl){
25812 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25813 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25815 this.previewEl.setLeft(pw);
25816 this.previewEl.setTop(ph);
25820 onMouseDown : function(e)
25824 this.dragable = true;
25825 this.pinching = false;
25827 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25828 this.dragable = false;
25832 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25833 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25837 onMouseMove : function(e)
25841 if(!this.canvasLoaded){
25845 if (!this.dragable){
25849 var minX = Math.ceil(this.thumbEl.getLeft(true));
25850 var minY = Math.ceil(this.thumbEl.getTop(true));
25852 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25853 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25855 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25856 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25858 x = x - this.mouseX;
25859 y = y - this.mouseY;
25861 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25862 var bgY = Math.ceil(y + this.previewEl.getTop(true));
25864 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25865 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25867 this.previewEl.setLeft(bgX);
25868 this.previewEl.setTop(bgY);
25870 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25871 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25874 onMouseUp : function(e)
25878 this.dragable = false;
25881 onMouseWheel : function(e)
25885 this.startScale = this.scale;
25887 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25889 if(!this.zoomable()){
25890 this.scale = this.startScale;
25899 zoomable : function()
25901 var minScale = this.thumbEl.getWidth() / this.minWidth;
25903 if(this.minWidth < this.minHeight){
25904 minScale = this.thumbEl.getHeight() / this.minHeight;
25907 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25908 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25912 (this.rotate == 0 || this.rotate == 180) &&
25914 width > this.imageEl.OriginWidth ||
25915 height > this.imageEl.OriginHeight ||
25916 (width < this.minWidth && height < this.minHeight)
25924 (this.rotate == 90 || this.rotate == 270) &&
25926 width > this.imageEl.OriginWidth ||
25927 height > this.imageEl.OriginHeight ||
25928 (width < this.minHeight && height < this.minWidth)
25935 !this.isDocument &&
25936 (this.rotate == 0 || this.rotate == 180) &&
25938 width < this.minWidth ||
25939 width > this.imageEl.OriginWidth ||
25940 height < this.minHeight ||
25941 height > this.imageEl.OriginHeight
25948 !this.isDocument &&
25949 (this.rotate == 90 || this.rotate == 270) &&
25951 width < this.minHeight ||
25952 width > this.imageEl.OriginWidth ||
25953 height < this.minWidth ||
25954 height > this.imageEl.OriginHeight
25964 onRotateLeft : function(e)
25966 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25968 var minScale = this.thumbEl.getWidth() / this.minWidth;
25970 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25971 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25973 this.startScale = this.scale;
25975 while (this.getScaleLevel() < minScale){
25977 this.scale = this.scale + 1;
25979 if(!this.zoomable()){
25984 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25985 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25990 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25997 this.scale = this.startScale;
25999 this.onRotateFail();
26004 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26006 if(this.isDocument){
26007 this.setThumbBoxSize();
26008 this.setThumbBoxPosition();
26009 this.setCanvasPosition();
26014 this.fireEvent('rotate', this, 'left');
26018 onRotateRight : function(e)
26020 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26022 var minScale = this.thumbEl.getWidth() / this.minWidth;
26024 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26025 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26027 this.startScale = this.scale;
26029 while (this.getScaleLevel() < minScale){
26031 this.scale = this.scale + 1;
26033 if(!this.zoomable()){
26038 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26039 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26044 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26051 this.scale = this.startScale;
26053 this.onRotateFail();
26058 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26060 if(this.isDocument){
26061 this.setThumbBoxSize();
26062 this.setThumbBoxPosition();
26063 this.setCanvasPosition();
26068 this.fireEvent('rotate', this, 'right');
26071 onRotateFail : function()
26073 this.errorEl.show(true);
26077 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26082 this.previewEl.dom.innerHTML = '';
26084 var canvasEl = document.createElement("canvas");
26086 var contextEl = canvasEl.getContext("2d");
26088 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26089 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26090 var center = this.imageEl.OriginWidth / 2;
26092 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26093 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26094 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26095 center = this.imageEl.OriginHeight / 2;
26098 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26100 contextEl.translate(center, center);
26101 contextEl.rotate(this.rotate * Math.PI / 180);
26103 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26105 this.canvasEl = document.createElement("canvas");
26107 this.contextEl = this.canvasEl.getContext("2d");
26109 switch (this.rotate) {
26112 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26113 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26115 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26120 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26121 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26123 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26124 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);
26128 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26133 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26134 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26136 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26137 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);
26141 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);
26146 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26147 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26149 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26150 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26154 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);
26161 this.previewEl.appendChild(this.canvasEl);
26163 this.setCanvasPosition();
26168 if(!this.canvasLoaded){
26172 var imageCanvas = document.createElement("canvas");
26174 var imageContext = imageCanvas.getContext("2d");
26176 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26177 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26179 var center = imageCanvas.width / 2;
26181 imageContext.translate(center, center);
26183 imageContext.rotate(this.rotate * Math.PI / 180);
26185 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26187 var canvas = document.createElement("canvas");
26189 var context = canvas.getContext("2d");
26191 canvas.width = this.minWidth;
26192 canvas.height = this.minHeight;
26194 switch (this.rotate) {
26197 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26198 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26200 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26201 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26203 var targetWidth = this.minWidth - 2 * x;
26204 var targetHeight = this.minHeight - 2 * y;
26208 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26209 scale = targetWidth / width;
26212 if(x > 0 && y == 0){
26213 scale = targetHeight / height;
26216 if(x > 0 && y > 0){
26217 scale = targetWidth / width;
26219 if(width < height){
26220 scale = targetHeight / height;
26224 context.scale(scale, scale);
26226 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26227 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26229 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26230 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26232 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26237 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26238 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26240 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26241 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26243 var targetWidth = this.minWidth - 2 * x;
26244 var targetHeight = this.minHeight - 2 * y;
26248 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26249 scale = targetWidth / width;
26252 if(x > 0 && y == 0){
26253 scale = targetHeight / height;
26256 if(x > 0 && y > 0){
26257 scale = targetWidth / width;
26259 if(width < height){
26260 scale = targetHeight / height;
26264 context.scale(scale, scale);
26266 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26267 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26269 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26270 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26272 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26274 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26279 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26280 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26282 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26283 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26285 var targetWidth = this.minWidth - 2 * x;
26286 var targetHeight = this.minHeight - 2 * y;
26290 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26291 scale = targetWidth / width;
26294 if(x > 0 && y == 0){
26295 scale = targetHeight / height;
26298 if(x > 0 && y > 0){
26299 scale = targetWidth / width;
26301 if(width < height){
26302 scale = targetHeight / height;
26306 context.scale(scale, scale);
26308 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26309 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26311 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26312 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26314 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26315 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26317 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26322 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26323 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26325 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26326 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26328 var targetWidth = this.minWidth - 2 * x;
26329 var targetHeight = this.minHeight - 2 * y;
26333 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26334 scale = targetWidth / width;
26337 if(x > 0 && y == 0){
26338 scale = targetHeight / height;
26341 if(x > 0 && y > 0){
26342 scale = targetWidth / width;
26344 if(width < height){
26345 scale = targetHeight / height;
26349 context.scale(scale, scale);
26351 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26352 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26354 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26355 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26357 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26359 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26366 this.cropData = canvas.toDataURL(this.cropType);
26368 if(this.fireEvent('crop', this, this.cropData) !== false){
26369 this.process(this.file, this.cropData);
26376 setThumbBoxSize : function()
26380 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26381 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26382 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26384 this.minWidth = width;
26385 this.minHeight = height;
26387 if(this.rotate == 90 || this.rotate == 270){
26388 this.minWidth = height;
26389 this.minHeight = width;
26394 width = Math.ceil(this.minWidth * height / this.minHeight);
26396 if(this.minWidth > this.minHeight){
26398 height = Math.ceil(this.minHeight * width / this.minWidth);
26401 this.thumbEl.setStyle({
26402 width : width + 'px',
26403 height : height + 'px'
26410 setThumbBoxPosition : function()
26412 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26413 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26415 this.thumbEl.setLeft(x);
26416 this.thumbEl.setTop(y);
26420 baseRotateLevel : function()
26422 this.baseRotate = 1;
26425 typeof(this.exif) != 'undefined' &&
26426 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26427 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26429 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26432 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26436 baseScaleLevel : function()
26440 if(this.isDocument){
26442 if(this.baseRotate == 6 || this.baseRotate == 8){
26444 height = this.thumbEl.getHeight();
26445 this.baseScale = height / this.imageEl.OriginWidth;
26447 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26448 width = this.thumbEl.getWidth();
26449 this.baseScale = width / this.imageEl.OriginHeight;
26455 height = this.thumbEl.getHeight();
26456 this.baseScale = height / this.imageEl.OriginHeight;
26458 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26459 width = this.thumbEl.getWidth();
26460 this.baseScale = width / this.imageEl.OriginWidth;
26466 if(this.baseRotate == 6 || this.baseRotate == 8){
26468 width = this.thumbEl.getHeight();
26469 this.baseScale = width / this.imageEl.OriginHeight;
26471 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26472 height = this.thumbEl.getWidth();
26473 this.baseScale = height / this.imageEl.OriginHeight;
26476 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26477 height = this.thumbEl.getWidth();
26478 this.baseScale = height / this.imageEl.OriginHeight;
26480 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26481 width = this.thumbEl.getHeight();
26482 this.baseScale = width / this.imageEl.OriginWidth;
26489 width = this.thumbEl.getWidth();
26490 this.baseScale = width / this.imageEl.OriginWidth;
26492 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26493 height = this.thumbEl.getHeight();
26494 this.baseScale = height / this.imageEl.OriginHeight;
26497 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26499 height = this.thumbEl.getHeight();
26500 this.baseScale = height / this.imageEl.OriginHeight;
26502 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26503 width = this.thumbEl.getWidth();
26504 this.baseScale = width / this.imageEl.OriginWidth;
26512 getScaleLevel : function()
26514 return this.baseScale * Math.pow(1.1, this.scale);
26517 onTouchStart : function(e)
26519 if(!this.canvasLoaded){
26520 this.beforeSelectFile(e);
26524 var touches = e.browserEvent.touches;
26530 if(touches.length == 1){
26531 this.onMouseDown(e);
26535 if(touches.length != 2){
26541 for(var i = 0, finger; finger = touches[i]; i++){
26542 coords.push(finger.pageX, finger.pageY);
26545 var x = Math.pow(coords[0] - coords[2], 2);
26546 var y = Math.pow(coords[1] - coords[3], 2);
26548 this.startDistance = Math.sqrt(x + y);
26550 this.startScale = this.scale;
26552 this.pinching = true;
26553 this.dragable = false;
26557 onTouchMove : function(e)
26559 if(!this.pinching && !this.dragable){
26563 var touches = e.browserEvent.touches;
26570 this.onMouseMove(e);
26576 for(var i = 0, finger; finger = touches[i]; i++){
26577 coords.push(finger.pageX, finger.pageY);
26580 var x = Math.pow(coords[0] - coords[2], 2);
26581 var y = Math.pow(coords[1] - coords[3], 2);
26583 this.endDistance = Math.sqrt(x + y);
26585 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26587 if(!this.zoomable()){
26588 this.scale = this.startScale;
26596 onTouchEnd : function(e)
26598 this.pinching = false;
26599 this.dragable = false;
26603 process : function(file, crop)
26606 this.maskEl.mask(this.loadingText);
26609 this.xhr = new XMLHttpRequest();
26611 file.xhr = this.xhr;
26613 this.xhr.open(this.method, this.url, true);
26616 "Accept": "application/json",
26617 "Cache-Control": "no-cache",
26618 "X-Requested-With": "XMLHttpRequest"
26621 for (var headerName in headers) {
26622 var headerValue = headers[headerName];
26624 this.xhr.setRequestHeader(headerName, headerValue);
26630 this.xhr.onload = function()
26632 _this.xhrOnLoad(_this.xhr);
26635 this.xhr.onerror = function()
26637 _this.xhrOnError(_this.xhr);
26640 var formData = new FormData();
26642 formData.append('returnHTML', 'NO');
26645 formData.append('crop', crop);
26648 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26649 formData.append(this.paramName, file, file.name);
26652 if(typeof(file.filename) != 'undefined'){
26653 formData.append('filename', file.filename);
26656 if(typeof(file.mimetype) != 'undefined'){
26657 formData.append('mimetype', file.mimetype);
26660 if(this.fireEvent('arrange', this, formData) != false){
26661 this.xhr.send(formData);
26665 xhrOnLoad : function(xhr)
26668 this.maskEl.unmask();
26671 if (xhr.readyState !== 4) {
26672 this.fireEvent('exception', this, xhr);
26676 var response = Roo.decode(xhr.responseText);
26678 if(!response.success){
26679 this.fireEvent('exception', this, xhr);
26683 var response = Roo.decode(xhr.responseText);
26685 this.fireEvent('upload', this, response);
26689 xhrOnError : function()
26692 this.maskEl.unmask();
26695 Roo.log('xhr on error');
26697 var response = Roo.decode(xhr.responseText);
26703 prepare : function(file)
26706 this.maskEl.mask(this.loadingText);
26712 if(typeof(file) === 'string'){
26713 this.loadCanvas(file);
26717 if(!file || !this.urlAPI){
26722 this.cropType = file.type;
26726 if(this.fireEvent('prepare', this, this.file) != false){
26728 var reader = new FileReader();
26730 reader.onload = function (e) {
26731 if (e.target.error) {
26732 Roo.log(e.target.error);
26736 var buffer = e.target.result,
26737 dataView = new DataView(buffer),
26739 maxOffset = dataView.byteLength - 4,
26743 if (dataView.getUint16(0) === 0xffd8) {
26744 while (offset < maxOffset) {
26745 markerBytes = dataView.getUint16(offset);
26747 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26748 markerLength = dataView.getUint16(offset + 2) + 2;
26749 if (offset + markerLength > dataView.byteLength) {
26750 Roo.log('Invalid meta data: Invalid segment size.');
26754 if(markerBytes == 0xffe1){
26755 _this.parseExifData(
26762 offset += markerLength;
26772 var url = _this.urlAPI.createObjectURL(_this.file);
26774 _this.loadCanvas(url);
26779 reader.readAsArrayBuffer(this.file);
26785 parseExifData : function(dataView, offset, length)
26787 var tiffOffset = offset + 10,
26791 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26792 // No Exif data, might be XMP data instead
26796 // Check for the ASCII code for "Exif" (0x45786966):
26797 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26798 // No Exif data, might be XMP data instead
26801 if (tiffOffset + 8 > dataView.byteLength) {
26802 Roo.log('Invalid Exif data: Invalid segment size.');
26805 // Check for the two null bytes:
26806 if (dataView.getUint16(offset + 8) !== 0x0000) {
26807 Roo.log('Invalid Exif data: Missing byte alignment offset.');
26810 // Check the byte alignment:
26811 switch (dataView.getUint16(tiffOffset)) {
26813 littleEndian = true;
26816 littleEndian = false;
26819 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26822 // Check for the TIFF tag marker (0x002A):
26823 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26824 Roo.log('Invalid Exif data: Missing TIFF marker.');
26827 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26828 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26830 this.parseExifTags(
26833 tiffOffset + dirOffset,
26838 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26843 if (dirOffset + 6 > dataView.byteLength) {
26844 Roo.log('Invalid Exif data: Invalid directory offset.');
26847 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26848 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26849 if (dirEndOffset + 4 > dataView.byteLength) {
26850 Roo.log('Invalid Exif data: Invalid directory size.');
26853 for (i = 0; i < tagsNumber; i += 1) {
26857 dirOffset + 2 + 12 * i, // tag offset
26861 // Return the offset to the next directory:
26862 return dataView.getUint32(dirEndOffset, littleEndian);
26865 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
26867 var tag = dataView.getUint16(offset, littleEndian);
26869 this.exif[tag] = this.getExifValue(
26873 dataView.getUint16(offset + 2, littleEndian), // tag type
26874 dataView.getUint32(offset + 4, littleEndian), // tag length
26879 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26881 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26890 Roo.log('Invalid Exif data: Invalid tag type.');
26894 tagSize = tagType.size * length;
26895 // Determine if the value is contained in the dataOffset bytes,
26896 // or if the value at the dataOffset is a pointer to the actual data:
26897 dataOffset = tagSize > 4 ?
26898 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26899 if (dataOffset + tagSize > dataView.byteLength) {
26900 Roo.log('Invalid Exif data: Invalid data offset.');
26903 if (length === 1) {
26904 return tagType.getValue(dataView, dataOffset, littleEndian);
26907 for (i = 0; i < length; i += 1) {
26908 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26911 if (tagType.ascii) {
26913 // Concatenate the chars:
26914 for (i = 0; i < values.length; i += 1) {
26916 // Ignore the terminating NULL byte(s):
26917 if (c === '\u0000') {
26929 Roo.apply(Roo.bootstrap.UploadCropbox, {
26931 'Orientation': 0x0112
26935 1: 0, //'top-left',
26937 3: 180, //'bottom-right',
26938 // 4: 'bottom-left',
26940 6: 90, //'right-top',
26941 // 7: 'right-bottom',
26942 8: 270 //'left-bottom'
26946 // byte, 8-bit unsigned int:
26948 getValue: function (dataView, dataOffset) {
26949 return dataView.getUint8(dataOffset);
26953 // ascii, 8-bit byte:
26955 getValue: function (dataView, dataOffset) {
26956 return String.fromCharCode(dataView.getUint8(dataOffset));
26961 // short, 16 bit int:
26963 getValue: function (dataView, dataOffset, littleEndian) {
26964 return dataView.getUint16(dataOffset, littleEndian);
26968 // long, 32 bit int:
26970 getValue: function (dataView, dataOffset, littleEndian) {
26971 return dataView.getUint32(dataOffset, littleEndian);
26975 // rational = two long values, first is numerator, second is denominator:
26977 getValue: function (dataView, dataOffset, littleEndian) {
26978 return dataView.getUint32(dataOffset, littleEndian) /
26979 dataView.getUint32(dataOffset + 4, littleEndian);
26983 // slong, 32 bit signed int:
26985 getValue: function (dataView, dataOffset, littleEndian) {
26986 return dataView.getInt32(dataOffset, littleEndian);
26990 // srational, two slongs, first is numerator, second is denominator:
26992 getValue: function (dataView, dataOffset, littleEndian) {
26993 return dataView.getInt32(dataOffset, littleEndian) /
26994 dataView.getInt32(dataOffset + 4, littleEndian);
27004 cls : 'btn-group roo-upload-cropbox-rotate-left',
27005 action : 'rotate-left',
27009 cls : 'btn btn-default',
27010 html : '<i class="fa fa-undo"></i>'
27016 cls : 'btn-group roo-upload-cropbox-picture',
27017 action : 'picture',
27021 cls : 'btn btn-default',
27022 html : '<i class="fa fa-picture-o"></i>'
27028 cls : 'btn-group roo-upload-cropbox-rotate-right',
27029 action : 'rotate-right',
27033 cls : 'btn btn-default',
27034 html : '<i class="fa fa-repeat"></i>'
27042 cls : 'btn-group roo-upload-cropbox-rotate-left',
27043 action : 'rotate-left',
27047 cls : 'btn btn-default',
27048 html : '<i class="fa fa-undo"></i>'
27054 cls : 'btn-group roo-upload-cropbox-download',
27055 action : 'download',
27059 cls : 'btn btn-default',
27060 html : '<i class="fa fa-download"></i>'
27066 cls : 'btn-group roo-upload-cropbox-crop',
27071 cls : 'btn btn-default',
27072 html : '<i class="fa fa-crop"></i>'
27078 cls : 'btn-group roo-upload-cropbox-trash',
27083 cls : 'btn btn-default',
27084 html : '<i class="fa fa-trash"></i>'
27090 cls : 'btn-group roo-upload-cropbox-rotate-right',
27091 action : 'rotate-right',
27095 cls : 'btn btn-default',
27096 html : '<i class="fa fa-repeat"></i>'
27104 cls : 'btn-group roo-upload-cropbox-rotate-left',
27105 action : 'rotate-left',
27109 cls : 'btn btn-default',
27110 html : '<i class="fa fa-undo"></i>'
27116 cls : 'btn-group roo-upload-cropbox-rotate-right',
27117 action : 'rotate-right',
27121 cls : 'btn btn-default',
27122 html : '<i class="fa fa-repeat"></i>'
27135 * @class Roo.bootstrap.DocumentManager
27136 * @extends Roo.bootstrap.Component
27137 * Bootstrap DocumentManager class
27138 * @cfg {String} paramName default 'imageUpload'
27139 * @cfg {String} method default POST
27140 * @cfg {String} url action url
27141 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27142 * @cfg {Boolean} multiple multiple upload default true
27143 * @cfg {Number} thumbSize default 300
27144 * @cfg {String} fieldLabel
27145 * @cfg {Number} labelWidth default 4
27146 * @cfg {String} labelAlign (left|top) default left
27147 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27150 * Create a new DocumentManager
27151 * @param {Object} config The config object
27154 Roo.bootstrap.DocumentManager = function(config){
27155 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27160 * Fire when initial the DocumentManager
27161 * @param {Roo.bootstrap.DocumentManager} this
27166 * inspect selected file
27167 * @param {Roo.bootstrap.DocumentManager} this
27168 * @param {File} file
27173 * Fire when xhr load exception
27174 * @param {Roo.bootstrap.DocumentManager} this
27175 * @param {XMLHttpRequest} xhr
27177 "exception" : true,
27180 * prepare the form data
27181 * @param {Roo.bootstrap.DocumentManager} this
27182 * @param {Object} formData
27187 * Fire when remove the file
27188 * @param {Roo.bootstrap.DocumentManager} this
27189 * @param {Object} file
27194 * Fire after refresh the file
27195 * @param {Roo.bootstrap.DocumentManager} this
27200 * Fire after click the image
27201 * @param {Roo.bootstrap.DocumentManager} this
27202 * @param {Object} file
27207 * Fire when upload a image and editable set to true
27208 * @param {Roo.bootstrap.DocumentManager} this
27209 * @param {Object} file
27213 * @event beforeselectfile
27214 * Fire before select file
27215 * @param {Roo.bootstrap.DocumentManager} this
27217 "beforeselectfile" : true,
27220 * Fire before process file
27221 * @param {Roo.bootstrap.DocumentManager} this
27222 * @param {Object} file
27229 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
27238 paramName : 'imageUpload',
27241 labelAlign : 'left',
27248 getAutoCreate : function()
27250 var managerWidget = {
27252 cls : 'roo-document-manager',
27256 cls : 'roo-document-manager-selector',
27261 cls : 'roo-document-manager-uploader',
27265 cls : 'roo-document-manager-upload-btn',
27266 html : '<i class="fa fa-plus"></i>'
27277 cls : 'column col-md-12',
27282 if(this.fieldLabel.length){
27287 cls : 'column col-md-12',
27288 html : this.fieldLabel
27292 cls : 'column col-md-12',
27297 if(this.labelAlign == 'left'){
27301 cls : 'column col-md-' + this.labelWidth,
27302 html : this.fieldLabel
27306 cls : 'column col-md-' + (12 - this.labelWidth),
27316 cls : 'row clearfix',
27324 initEvents : function()
27326 this.managerEl = this.el.select('.roo-document-manager', true).first();
27327 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27329 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27330 this.selectorEl.hide();
27333 this.selectorEl.attr('multiple', 'multiple');
27336 this.selectorEl.on('change', this.onFileSelected, this);
27338 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27339 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27341 this.uploader.on('click', this.onUploaderClick, this);
27343 this.renderProgressDialog();
27347 window.addEventListener("resize", function() { _this.refresh(); } );
27349 this.fireEvent('initial', this);
27352 renderProgressDialog : function()
27356 this.progressDialog = new Roo.bootstrap.Modal({
27357 cls : 'roo-document-manager-progress-dialog',
27358 allow_close : false,
27368 btnclick : function() {
27369 _this.uploadCancel();
27375 this.progressDialog.render(Roo.get(document.body));
27377 this.progress = new Roo.bootstrap.Progress({
27378 cls : 'roo-document-manager-progress',
27383 this.progress.render(this.progressDialog.getChildContainer());
27385 this.progressBar = new Roo.bootstrap.ProgressBar({
27386 cls : 'roo-document-manager-progress-bar',
27389 aria_valuemax : 12,
27393 this.progressBar.render(this.progress.getChildContainer());
27396 onUploaderClick : function(e)
27398 e.preventDefault();
27400 if(this.fireEvent('beforeselectfile', this) != false){
27401 this.selectorEl.dom.click();
27406 onFileSelected : function(e)
27408 e.preventDefault();
27410 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27414 Roo.each(this.selectorEl.dom.files, function(file){
27415 if(this.fireEvent('inspect', this, file) != false){
27416 this.files.push(file);
27426 this.selectorEl.dom.value = '';
27428 if(!this.files.length){
27432 if(this.boxes > 0 && this.files.length > this.boxes){
27433 this.files = this.files.slice(0, this.boxes);
27436 this.uploader.show();
27438 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27439 this.uploader.hide();
27448 Roo.each(this.files, function(file){
27450 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27451 var f = this.renderPreview(file);
27456 if(file.type.indexOf('image') != -1){
27457 this.delegates.push(
27459 _this.process(file);
27460 }).createDelegate(this)
27468 _this.process(file);
27469 }).createDelegate(this)
27474 this.files = files;
27476 this.delegates = this.delegates.concat(docs);
27478 if(!this.delegates.length){
27483 this.progressBar.aria_valuemax = this.delegates.length;
27490 arrange : function()
27492 if(!this.delegates.length){
27493 this.progressDialog.hide();
27498 var delegate = this.delegates.shift();
27500 this.progressDialog.show();
27502 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27504 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27509 refresh : function()
27511 this.uploader.show();
27513 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27514 this.uploader.hide();
27517 Roo.isTouch ? this.closable(false) : this.closable(true);
27519 this.fireEvent('refresh', this);
27522 onRemove : function(e, el, o)
27524 e.preventDefault();
27526 this.fireEvent('remove', this, o);
27530 remove : function(o)
27534 Roo.each(this.files, function(file){
27535 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27544 this.files = files;
27551 Roo.each(this.files, function(file){
27556 file.target.remove();
27565 onClick : function(e, el, o)
27567 e.preventDefault();
27569 this.fireEvent('click', this, o);
27573 closable : function(closable)
27575 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27577 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27589 xhrOnLoad : function(xhr)
27591 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27595 if (xhr.readyState !== 4) {
27597 this.fireEvent('exception', this, xhr);
27601 var response = Roo.decode(xhr.responseText);
27603 if(!response.success){
27605 this.fireEvent('exception', this, xhr);
27609 var file = this.renderPreview(response.data);
27611 this.files.push(file);
27617 xhrOnError : function(xhr)
27619 Roo.log('xhr on error');
27621 var response = Roo.decode(xhr.responseText);
27628 process : function(file)
27630 if(this.fireEvent('process', this, file) !== false){
27631 if(this.editable && file.type.indexOf('image') != -1){
27632 this.fireEvent('edit', this, file);
27636 this.uploadStart(file, false);
27643 uploadStart : function(file, crop)
27645 this.xhr = new XMLHttpRequest();
27647 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27652 file.xhr = this.xhr;
27654 this.managerEl.createChild({
27656 cls : 'roo-document-manager-loading',
27660 tooltip : file.name,
27661 cls : 'roo-document-manager-thumb',
27662 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27668 this.xhr.open(this.method, this.url, true);
27671 "Accept": "application/json",
27672 "Cache-Control": "no-cache",
27673 "X-Requested-With": "XMLHttpRequest"
27676 for (var headerName in headers) {
27677 var headerValue = headers[headerName];
27679 this.xhr.setRequestHeader(headerName, headerValue);
27685 this.xhr.onload = function()
27687 _this.xhrOnLoad(_this.xhr);
27690 this.xhr.onerror = function()
27692 _this.xhrOnError(_this.xhr);
27695 var formData = new FormData();
27697 formData.append('returnHTML', 'NO');
27700 formData.append('crop', crop);
27703 formData.append(this.paramName, file, file.name);
27705 if(this.fireEvent('prepare', this, formData) != false){
27706 this.xhr.send(formData);
27710 uploadCancel : function()
27717 this.delegates = [];
27719 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27726 renderPreview : function(file)
27728 if(typeof(file.target) != 'undefined' && file.target){
27732 var previewEl = this.managerEl.createChild({
27734 cls : 'roo-document-manager-preview',
27738 tooltip : file.filename,
27739 cls : 'roo-document-manager-thumb',
27740 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27745 html : '<i class="fa fa-times-circle"></i>'
27750 var close = previewEl.select('button.close', true).first();
27752 close.on('click', this.onRemove, this, file);
27754 file.target = previewEl;
27756 var image = previewEl.select('img', true).first();
27760 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27762 image.on('click', this.onClick, this, file);
27768 onPreviewLoad : function(file, image)
27770 if(typeof(file.target) == 'undefined' || !file.target){
27774 var width = image.dom.naturalWidth || image.dom.width;
27775 var height = image.dom.naturalHeight || image.dom.height;
27777 if(width > height){
27778 file.target.addClass('wide');
27782 file.target.addClass('tall');
27787 uploadFromSource : function(file, crop)
27789 this.xhr = new XMLHttpRequest();
27791 this.managerEl.createChild({
27793 cls : 'roo-document-manager-loading',
27797 tooltip : file.name,
27798 cls : 'roo-document-manager-thumb',
27799 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27805 this.xhr.open(this.method, this.url, true);
27808 "Accept": "application/json",
27809 "Cache-Control": "no-cache",
27810 "X-Requested-With": "XMLHttpRequest"
27813 for (var headerName in headers) {
27814 var headerValue = headers[headerName];
27816 this.xhr.setRequestHeader(headerName, headerValue);
27822 this.xhr.onload = function()
27824 _this.xhrOnLoad(_this.xhr);
27827 this.xhr.onerror = function()
27829 _this.xhrOnError(_this.xhr);
27832 var formData = new FormData();
27834 formData.append('returnHTML', 'NO');
27836 formData.append('crop', crop);
27838 if(typeof(file.filename) != 'undefined'){
27839 formData.append('filename', file.filename);
27842 if(typeof(file.mimetype) != 'undefined'){
27843 formData.append('mimetype', file.mimetype);
27846 if(this.fireEvent('prepare', this, formData) != false){
27847 this.xhr.send(formData);
27857 * @class Roo.bootstrap.DocumentViewer
27858 * @extends Roo.bootstrap.Component
27859 * Bootstrap DocumentViewer class
27862 * Create a new DocumentViewer
27863 * @param {Object} config The config object
27866 Roo.bootstrap.DocumentViewer = function(config){
27867 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27872 * Fire after initEvent
27873 * @param {Roo.bootstrap.DocumentViewer} this
27879 * @param {Roo.bootstrap.DocumentViewer} this
27884 * Fire after trash button
27885 * @param {Roo.bootstrap.DocumentViewer} this
27892 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27894 getAutoCreate : function()
27898 cls : 'roo-document-viewer',
27902 cls : 'roo-document-viewer-body',
27906 cls : 'roo-document-viewer-thumb',
27910 cls : 'roo-document-viewer-image'
27918 cls : 'roo-document-viewer-footer',
27921 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27929 cls : 'btn btn-default roo-document-viewer-trash',
27930 html : '<i class="fa fa-trash"></i>'
27943 initEvents : function()
27946 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27947 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27949 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27950 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27952 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27953 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27955 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27956 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27958 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27959 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27961 this.bodyEl.on('click', this.onClick, this);
27963 this.trashBtn.on('click', this.onTrash, this);
27967 initial : function()
27969 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27972 this.fireEvent('initial', this);
27976 onClick : function(e)
27978 e.preventDefault();
27980 this.fireEvent('click', this);
27983 onTrash : function(e)
27985 e.preventDefault();
27987 this.fireEvent('trash', this);
27999 * @class Roo.bootstrap.NavProgressBar
28000 * @extends Roo.bootstrap.Component
28001 * Bootstrap NavProgressBar class
28004 * Create a new nav progress bar
28005 * @param {Object} config The config object
28008 Roo.bootstrap.NavProgressBar = function(config){
28009 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28011 this.bullets = this.bullets || [];
28013 // Roo.bootstrap.NavProgressBar.register(this);
28017 * Fires when the active item changes
28018 * @param {Roo.bootstrap.NavProgressBar} this
28019 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28020 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
28027 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
28032 getAutoCreate : function()
28034 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28038 cls : 'roo-navigation-bar-group',
28042 cls : 'roo-navigation-top-bar'
28046 cls : 'roo-navigation-bullets-bar',
28050 cls : 'roo-navigation-bar'
28057 cls : 'roo-navigation-bottom-bar'
28067 initEvents: function()
28072 onRender : function(ct, position)
28074 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28076 if(this.bullets.length){
28077 Roo.each(this.bullets, function(b){
28086 addItem : function(cfg)
28088 var item = new Roo.bootstrap.NavProgressItem(cfg);
28090 item.parentId = this.id;
28091 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28094 var top = new Roo.bootstrap.Element({
28096 cls : 'roo-navigation-bar-text'
28099 var bottom = new Roo.bootstrap.Element({
28101 cls : 'roo-navigation-bar-text'
28104 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28105 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28107 var topText = new Roo.bootstrap.Element({
28109 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28112 var bottomText = new Roo.bootstrap.Element({
28114 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28117 topText.onRender(top.el, null);
28118 bottomText.onRender(bottom.el, null);
28121 item.bottomEl = bottom;
28124 this.barItems.push(item);
28129 getActive : function()
28131 var active = false;
28133 Roo.each(this.barItems, function(v){
28135 if (!v.isActive()) {
28147 setActiveItem : function(item)
28151 Roo.each(this.barItems, function(v){
28152 if (v.rid == item.rid) {
28156 if (v.isActive()) {
28157 v.setActive(false);
28162 item.setActive(true);
28164 this.fireEvent('changed', this, item, prev);
28167 getBarItem: function(rid)
28171 Roo.each(this.barItems, function(e) {
28172 if (e.rid != rid) {
28183 indexOfItem : function(item)
28187 Roo.each(this.barItems, function(v, i){
28189 if (v.rid != item.rid) {
28200 setActiveNext : function()
28202 var i = this.indexOfItem(this.getActive());
28204 if (i > this.barItems.length) {
28208 this.setActiveItem(this.barItems[i+1]);
28211 setActivePrev : function()
28213 var i = this.indexOfItem(this.getActive());
28219 this.setActiveItem(this.barItems[i-1]);
28222 format : function()
28224 if(!this.barItems.length){
28228 var width = 100 / this.barItems.length;
28230 Roo.each(this.barItems, function(i){
28231 i.el.setStyle('width', width + '%');
28232 i.topEl.el.setStyle('width', width + '%');
28233 i.bottomEl.el.setStyle('width', width + '%');
28242 * Nav Progress Item
28247 * @class Roo.bootstrap.NavProgressItem
28248 * @extends Roo.bootstrap.Component
28249 * Bootstrap NavProgressItem class
28250 * @cfg {String} rid the reference id
28251 * @cfg {Boolean} active (true|false) Is item active default false
28252 * @cfg {Boolean} disabled (true|false) Is item active default false
28253 * @cfg {String} html
28254 * @cfg {String} position (top|bottom) text position default bottom
28255 * @cfg {String} icon show icon instead of number
28258 * Create a new NavProgressItem
28259 * @param {Object} config The config object
28261 Roo.bootstrap.NavProgressItem = function(config){
28262 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28267 * The raw click event for the entire grid.
28268 * @param {Roo.bootstrap.NavProgressItem} this
28269 * @param {Roo.EventObject} e
28276 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
28282 position : 'bottom',
28285 getAutoCreate : function()
28287 var iconCls = 'roo-navigation-bar-item-icon';
28289 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28293 cls: 'roo-navigation-bar-item',
28303 cfg.cls += ' active';
28306 cfg.cls += ' disabled';
28312 disable : function()
28314 this.setDisabled(true);
28317 enable : function()
28319 this.setDisabled(false);
28322 initEvents: function()
28324 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28326 this.iconEl.on('click', this.onClick, this);
28329 onClick : function(e)
28331 e.preventDefault();
28337 if(this.fireEvent('click', this, e) === false){
28341 this.parent().setActiveItem(this);
28344 isActive: function ()
28346 return this.active;
28349 setActive : function(state)
28351 if(this.active == state){
28355 this.active = state;
28358 this.el.addClass('active');
28362 this.el.removeClass('active');
28367 setDisabled : function(state)
28369 if(this.disabled == state){
28373 this.disabled = state;
28376 this.el.addClass('disabled');
28380 this.el.removeClass('disabled');
28383 tooltipEl : function()
28385 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28398 * @class Roo.bootstrap.FieldLabel
28399 * @extends Roo.bootstrap.Component
28400 * Bootstrap FieldLabel class
28401 * @cfg {String} html contents of the element
28402 * @cfg {String} tag tag of the element default label
28403 * @cfg {String} cls class of the element
28404 * @cfg {String} target label target
28405 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28406 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28407 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28408 * @cfg {String} iconTooltip default "This field is required"
28411 * Create a new FieldLabel
28412 * @param {Object} config The config object
28415 Roo.bootstrap.FieldLabel = function(config){
28416 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28421 * Fires after the field has been marked as invalid.
28422 * @param {Roo.form.FieldLabel} this
28423 * @param {String} msg The validation message
28428 * Fires after the field has been validated with no errors.
28429 * @param {Roo.form.FieldLabel} this
28435 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28442 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28443 validClass : 'text-success fa fa-lg fa-check',
28444 iconTooltip : 'This field is required',
28446 getAutoCreate : function(){
28450 cls : 'roo-bootstrap-field-label ' + this.cls,
28456 tooltip : this.iconTooltip
28468 initEvents: function()
28470 Roo.bootstrap.Element.superclass.initEvents.call(this);
28472 this.iconEl = this.el.select('i', true).first();
28474 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28476 Roo.bootstrap.FieldLabel.register(this);
28480 * Mark this field as valid
28482 markValid : function()
28484 this.iconEl.show();
28486 this.iconEl.removeClass(this.invalidClass);
28488 this.iconEl.addClass(this.validClass);
28490 this.fireEvent('valid', this);
28494 * Mark this field as invalid
28495 * @param {String} msg The validation message
28497 markInvalid : function(msg)
28499 this.iconEl.show();
28501 this.iconEl.removeClass(this.validClass);
28503 this.iconEl.addClass(this.invalidClass);
28505 this.fireEvent('invalid', this, msg);
28511 Roo.apply(Roo.bootstrap.FieldLabel, {
28516 * register a FieldLabel Group
28517 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28519 register : function(label)
28521 if(this.groups.hasOwnProperty(label.target)){
28525 this.groups[label.target] = label;
28529 * fetch a FieldLabel Group based on the target
28530 * @param {string} target
28531 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28533 get: function(target) {
28534 if (typeof(this.groups[target]) == 'undefined') {
28538 return this.groups[target] ;
28547 * page DateSplitField.
28553 * @class Roo.bootstrap.DateSplitField
28554 * @extends Roo.bootstrap.Component
28555 * Bootstrap DateSplitField class
28556 * @cfg {string} fieldLabel - the label associated
28557 * @cfg {Number} labelWidth set the width of label (0-12)
28558 * @cfg {String} labelAlign (top|left)
28559 * @cfg {Boolean} dayAllowBlank (true|false) default false
28560 * @cfg {Boolean} monthAllowBlank (true|false) default false
28561 * @cfg {Boolean} yearAllowBlank (true|false) default false
28562 * @cfg {string} dayPlaceholder
28563 * @cfg {string} monthPlaceholder
28564 * @cfg {string} yearPlaceholder
28565 * @cfg {string} dayFormat default 'd'
28566 * @cfg {string} monthFormat default 'm'
28567 * @cfg {string} yearFormat default 'Y'
28571 * Create a new DateSplitField
28572 * @param {Object} config The config object
28575 Roo.bootstrap.DateSplitField = function(config){
28576 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28582 * getting the data of years
28583 * @param {Roo.bootstrap.DateSplitField} this
28584 * @param {Object} years
28589 * getting the data of days
28590 * @param {Roo.bootstrap.DateSplitField} this
28591 * @param {Object} days
28596 * Fires after the field has been marked as invalid.
28597 * @param {Roo.form.Field} this
28598 * @param {String} msg The validation message
28603 * Fires after the field has been validated with no errors.
28604 * @param {Roo.form.Field} this
28610 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28613 labelAlign : 'top',
28615 dayAllowBlank : false,
28616 monthAllowBlank : false,
28617 yearAllowBlank : false,
28618 dayPlaceholder : '',
28619 monthPlaceholder : '',
28620 yearPlaceholder : '',
28624 isFormField : true,
28626 getAutoCreate : function()
28630 cls : 'row roo-date-split-field-group',
28635 cls : 'form-hidden-field roo-date-split-field-group-value',
28641 if(this.fieldLabel){
28644 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28648 html : this.fieldLabel
28654 Roo.each(['day', 'month', 'year'], function(t){
28657 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28664 inputEl: function ()
28666 return this.el.select('.roo-date-split-field-group-value', true).first();
28669 onRender : function(ct, position)
28673 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28675 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28677 this.dayField = new Roo.bootstrap.ComboBox({
28678 allowBlank : this.dayAllowBlank,
28679 alwaysQuery : true,
28680 displayField : 'value',
28683 forceSelection : true,
28685 placeholder : this.dayPlaceholder,
28686 selectOnFocus : true,
28687 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28688 triggerAction : 'all',
28690 valueField : 'value',
28691 store : new Roo.data.SimpleStore({
28692 data : (function() {
28694 _this.fireEvent('days', _this, days);
28697 fields : [ 'value' ]
28700 select : function (_self, record, index)
28702 _this.setValue(_this.getValue());
28707 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28709 this.monthField = new Roo.bootstrap.MonthField({
28710 after : '<i class=\"fa fa-calendar\"></i>',
28711 allowBlank : this.monthAllowBlank,
28712 placeholder : this.monthPlaceholder,
28715 render : function (_self)
28717 this.el.select('span.input-group-addon', true).first().on('click', function(e){
28718 e.preventDefault();
28722 select : function (_self, oldvalue, newvalue)
28724 _this.setValue(_this.getValue());
28729 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28731 this.yearField = new Roo.bootstrap.ComboBox({
28732 allowBlank : this.yearAllowBlank,
28733 alwaysQuery : true,
28734 displayField : 'value',
28737 forceSelection : true,
28739 placeholder : this.yearPlaceholder,
28740 selectOnFocus : true,
28741 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28742 triggerAction : 'all',
28744 valueField : 'value',
28745 store : new Roo.data.SimpleStore({
28746 data : (function() {
28748 _this.fireEvent('years', _this, years);
28751 fields : [ 'value' ]
28754 select : function (_self, record, index)
28756 _this.setValue(_this.getValue());
28761 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28764 setValue : function(v, format)
28766 this.inputEl.dom.value = v;
28768 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28770 var d = Date.parseDate(v, f);
28777 this.setDay(d.format(this.dayFormat));
28778 this.setMonth(d.format(this.monthFormat));
28779 this.setYear(d.format(this.yearFormat));
28786 setDay : function(v)
28788 this.dayField.setValue(v);
28789 this.inputEl.dom.value = this.getValue();
28794 setMonth : function(v)
28796 this.monthField.setValue(v, true);
28797 this.inputEl.dom.value = this.getValue();
28802 setYear : function(v)
28804 this.yearField.setValue(v);
28805 this.inputEl.dom.value = this.getValue();
28810 getDay : function()
28812 return this.dayField.getValue();
28815 getMonth : function()
28817 return this.monthField.getValue();
28820 getYear : function()
28822 return this.yearField.getValue();
28825 getValue : function()
28827 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28829 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28839 this.inputEl.dom.value = '';
28844 validate : function()
28846 var d = this.dayField.validate();
28847 var m = this.monthField.validate();
28848 var y = this.yearField.validate();
28853 (!this.dayAllowBlank && !d) ||
28854 (!this.monthAllowBlank && !m) ||
28855 (!this.yearAllowBlank && !y)
28860 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28869 this.markInvalid();
28874 markValid : function()
28877 var label = this.el.select('label', true).first();
28878 var icon = this.el.select('i.fa-star', true).first();
28884 this.fireEvent('valid', this);
28888 * Mark this field as invalid
28889 * @param {String} msg The validation message
28891 markInvalid : function(msg)
28894 var label = this.el.select('label', true).first();
28895 var icon = this.el.select('i.fa-star', true).first();
28897 if(label && !icon){
28898 this.el.select('.roo-date-split-field-label', true).createChild({
28900 cls : 'text-danger fa fa-lg fa-star',
28901 tooltip : 'This field is required',
28902 style : 'margin-right:5px;'
28906 this.fireEvent('invalid', this, msg);
28909 clearInvalid : function()
28911 var label = this.el.select('label', true).first();
28912 var icon = this.el.select('i.fa-star', true).first();
28918 this.fireEvent('valid', this);
28921 getName: function()
28931 * http://masonry.desandro.com
28933 * The idea is to render all the bricks based on vertical width...
28935 * The original code extends 'outlayer' - we might need to use that....
28941 * @class Roo.bootstrap.LayoutMasonry
28942 * @extends Roo.bootstrap.Component
28943 * Bootstrap Layout Masonry class
28946 * Create a new Element
28947 * @param {Object} config The config object
28950 Roo.bootstrap.LayoutMasonry = function(config){
28951 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28957 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
28960 * @cfg {Boolean} isLayoutInstant = no animation?
28962 isLayoutInstant : false, // needed?
28965 * @cfg {Number} boxWidth width of the columns
28970 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
28975 * @cfg {Number} padWidth padding below box..
28980 * @cfg {Number} gutter gutter width..
28985 * @cfg {Number} maxCols maximum number of columns
28991 * @cfg {Boolean} isAutoInitial defalut true
28993 isAutoInitial : true,
28998 * @cfg {Boolean} isHorizontal defalut false
29000 isHorizontal : false,
29002 currentSize : null,
29008 bricks: null, //CompositeElement
29012 _isLayoutInited : false,
29014 // isAlternative : false, // only use for vertical layout...
29017 * @cfg {Number} alternativePadWidth padding below box..
29019 alternativePadWidth : 50,
29021 getAutoCreate : function(){
29025 cls: 'blog-masonary-wrapper ' + this.cls,
29027 cls : 'mas-boxes masonary'
29034 getChildContainer: function( )
29036 if (this.boxesEl) {
29037 return this.boxesEl;
29040 this.boxesEl = this.el.select('.mas-boxes').first();
29042 return this.boxesEl;
29046 initEvents : function()
29050 if(this.isAutoInitial){
29051 Roo.log('hook children rendered');
29052 this.on('childrenrendered', function() {
29053 Roo.log('children rendered');
29059 initial : function()
29061 this.currentSize = this.el.getBox(true);
29063 Roo.EventManager.onWindowResize(this.resize, this);
29065 if(!this.isAutoInitial){
29073 //this.layout.defer(500,this);
29077 resize : function()
29081 var cs = this.el.getBox(true);
29083 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29084 Roo.log("no change in with or X");
29088 this.currentSize = cs;
29094 layout : function()
29096 this._resetLayout();
29098 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29100 this.layoutItems( isInstant );
29102 this._isLayoutInited = true;
29106 _resetLayout : function()
29108 if(this.isHorizontal){
29109 this.horizontalMeasureColumns();
29113 this.verticalMeasureColumns();
29117 verticalMeasureColumns : function()
29119 this.getContainerWidth();
29121 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29122 // this.colWidth = Math.floor(this.containerWidth * 0.8);
29126 var boxWidth = this.boxWidth + this.padWidth;
29128 if(this.containerWidth < this.boxWidth){
29129 boxWidth = this.containerWidth
29132 var containerWidth = this.containerWidth;
29134 var cols = Math.floor(containerWidth / boxWidth);
29136 this.cols = Math.max( cols, 1 );
29138 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29140 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29142 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29144 this.colWidth = boxWidth + avail - this.padWidth;
29146 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29147 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
29150 horizontalMeasureColumns : function()
29152 this.getContainerWidth();
29154 var boxWidth = this.boxWidth;
29156 if(this.containerWidth < boxWidth){
29157 boxWidth = this.containerWidth;
29160 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29162 this.el.setHeight(boxWidth);
29166 getContainerWidth : function()
29168 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
29171 layoutItems : function( isInstant )
29173 var items = Roo.apply([], this.bricks);
29175 if(this.isHorizontal){
29176 this._horizontalLayoutItems( items , isInstant );
29180 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29181 // this._verticalAlternativeLayoutItems( items , isInstant );
29185 this._verticalLayoutItems( items , isInstant );
29189 _verticalLayoutItems : function ( items , isInstant)
29191 if ( !items || !items.length ) {
29196 ['xs', 'xs', 'xs', 'tall'],
29197 ['xs', 'xs', 'tall'],
29198 ['xs', 'xs', 'sm'],
29199 ['xs', 'xs', 'xs'],
29205 ['sm', 'xs', 'xs'],
29209 ['tall', 'xs', 'xs', 'xs'],
29210 ['tall', 'xs', 'xs'],
29222 Roo.each(items, function(item, k){
29224 switch (item.size) {
29225 // these layouts take up a full box,
29236 boxes.push([item]);
29259 var filterPattern = function(box, length)
29267 var pattern = box.slice(0, length);
29271 Roo.each(pattern, function(i){
29272 format.push(i.size);
29275 Roo.each(standard, function(s){
29277 if(String(s) != String(format)){
29286 if(!match && length == 1){
29291 filterPattern(box, length - 1);
29295 queue.push(pattern);
29297 box = box.slice(length, box.length);
29299 filterPattern(box, 4);
29305 Roo.each(boxes, function(box, k){
29311 if(box.length == 1){
29316 filterPattern(box, 4);
29320 this._processVerticalLayoutQueue( queue, isInstant );
29324 // _verticalAlternativeLayoutItems : function( items , isInstant )
29326 // if ( !items || !items.length ) {
29330 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
29334 _horizontalLayoutItems : function ( items , isInstant)
29336 if ( !items || !items.length || items.length < 3) {
29342 var eItems = items.slice(0, 3);
29344 items = items.slice(3, items.length);
29347 ['xs', 'xs', 'xs', 'wide'],
29348 ['xs', 'xs', 'wide'],
29349 ['xs', 'xs', 'sm'],
29350 ['xs', 'xs', 'xs'],
29356 ['sm', 'xs', 'xs'],
29360 ['wide', 'xs', 'xs', 'xs'],
29361 ['wide', 'xs', 'xs'],
29374 Roo.each(items, function(item, k){
29376 switch (item.size) {
29387 boxes.push([item]);
29411 var filterPattern = function(box, length)
29419 var pattern = box.slice(0, length);
29423 Roo.each(pattern, function(i){
29424 format.push(i.size);
29427 Roo.each(standard, function(s){
29429 if(String(s) != String(format)){
29438 if(!match && length == 1){
29443 filterPattern(box, length - 1);
29447 queue.push(pattern);
29449 box = box.slice(length, box.length);
29451 filterPattern(box, 4);
29457 Roo.each(boxes, function(box, k){
29463 if(box.length == 1){
29468 filterPattern(box, 4);
29475 var pos = this.el.getBox(true);
29479 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29481 var hit_end = false;
29483 Roo.each(queue, function(box){
29487 Roo.each(box, function(b){
29489 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29499 Roo.each(box, function(b){
29501 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29504 mx = Math.max(mx, b.x);
29508 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29512 Roo.each(box, function(b){
29514 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29528 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29531 /** Sets position of item in DOM
29532 * @param {Element} item
29533 * @param {Number} x - horizontal position
29534 * @param {Number} y - vertical position
29535 * @param {Boolean} isInstant - disables transitions
29537 _processVerticalLayoutQueue : function( queue, isInstant )
29539 var pos = this.el.getBox(true);
29544 for (var i = 0; i < this.cols; i++){
29548 Roo.each(queue, function(box, k){
29550 var col = k % this.cols;
29552 Roo.each(box, function(b,kk){
29554 b.el.position('absolute');
29556 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29557 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29559 if(b.size == 'md-left' || b.size == 'md-right'){
29560 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29561 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29564 b.el.setWidth(width);
29565 b.el.setHeight(height);
29567 b.el.select('iframe',true).setSize(width,height);
29571 for (var i = 0; i < this.cols; i++){
29573 if(maxY[i] < maxY[col]){
29578 col = Math.min(col, i);
29582 x = pos.x + col * (this.colWidth + this.padWidth);
29586 var positions = [];
29588 switch (box.length){
29590 positions = this.getVerticalOneBoxColPositions(x, y, box);
29593 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29596 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29599 positions = this.getVerticalFourBoxColPositions(x, y, box);
29605 Roo.each(box, function(b,kk){
29607 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29609 var sz = b.el.getSize();
29611 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29619 for (var i = 0; i < this.cols; i++){
29620 mY = Math.max(mY, maxY[i]);
29623 this.el.setHeight(mY - pos.y);
29627 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29629 // var pos = this.el.getBox(true);
29632 // var maxX = pos.right;
29634 // var maxHeight = 0;
29636 // Roo.each(items, function(item, k){
29640 // item.el.position('absolute');
29642 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29644 // item.el.setWidth(width);
29646 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29648 // item.el.setHeight(height);
29651 // item.el.setXY([x, y], isInstant ? false : true);
29653 // item.el.setXY([maxX - width, y], isInstant ? false : true);
29656 // y = y + height + this.alternativePadWidth;
29658 // maxHeight = maxHeight + height + this.alternativePadWidth;
29662 // this.el.setHeight(maxHeight);
29666 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29668 var pos = this.el.getBox(true);
29673 var maxX = pos.right;
29675 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29677 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29679 Roo.each(queue, function(box, k){
29681 Roo.each(box, function(b, kk){
29683 b.el.position('absolute');
29685 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29686 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29688 if(b.size == 'md-left' || b.size == 'md-right'){
29689 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29690 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29693 b.el.setWidth(width);
29694 b.el.setHeight(height);
29702 var positions = [];
29704 switch (box.length){
29706 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29709 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29712 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29715 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29721 Roo.each(box, function(b,kk){
29723 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29725 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29733 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29735 Roo.each(eItems, function(b,k){
29737 b.size = (k == 0) ? 'sm' : 'xs';
29738 b.x = (k == 0) ? 2 : 1;
29739 b.y = (k == 0) ? 2 : 1;
29741 b.el.position('absolute');
29743 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29745 b.el.setWidth(width);
29747 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29749 b.el.setHeight(height);
29753 var positions = [];
29756 x : maxX - this.unitWidth * 2 - this.gutter,
29761 x : maxX - this.unitWidth,
29762 y : minY + (this.unitWidth + this.gutter) * 2
29766 x : maxX - this.unitWidth * 3 - this.gutter * 2,
29770 Roo.each(eItems, function(b,k){
29772 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29778 getVerticalOneBoxColPositions : function(x, y, box)
29782 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29784 if(box[0].size == 'md-left'){
29788 if(box[0].size == 'md-right'){
29793 x : x + (this.unitWidth + this.gutter) * rand,
29800 getVerticalTwoBoxColPositions : function(x, y, box)
29804 if(box[0].size == 'xs'){
29808 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29812 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29826 x : x + (this.unitWidth + this.gutter) * 2,
29827 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29834 getVerticalThreeBoxColPositions : function(x, y, box)
29838 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29846 x : x + (this.unitWidth + this.gutter) * 1,
29851 x : x + (this.unitWidth + this.gutter) * 2,
29859 if(box[0].size == 'xs' && box[1].size == 'xs'){
29868 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29872 x : x + (this.unitWidth + this.gutter) * 1,
29886 x : x + (this.unitWidth + this.gutter) * 2,
29891 x : x + (this.unitWidth + this.gutter) * 2,
29892 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29899 getVerticalFourBoxColPositions : function(x, y, box)
29903 if(box[0].size == 'xs'){
29912 y : y + (this.unitHeight + this.gutter) * 1
29917 y : y + (this.unitHeight + this.gutter) * 2
29921 x : x + (this.unitWidth + this.gutter) * 1,
29935 x : x + (this.unitWidth + this.gutter) * 2,
29940 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29941 y : y + (this.unitHeight + this.gutter) * 1
29945 x : x + (this.unitWidth + this.gutter) * 2,
29946 y : y + (this.unitWidth + this.gutter) * 2
29953 getHorizontalOneBoxColPositions : function(maxX, minY, box)
29957 if(box[0].size == 'md-left'){
29959 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29966 if(box[0].size == 'md-right'){
29968 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29969 y : minY + (this.unitWidth + this.gutter) * 1
29975 var rand = Math.floor(Math.random() * (4 - box[0].y));
29978 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29979 y : minY + (this.unitWidth + this.gutter) * rand
29986 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29990 if(box[0].size == 'xs'){
29993 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29998 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29999 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30007 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30012 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30013 y : minY + (this.unitWidth + this.gutter) * 2
30020 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30024 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30027 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30032 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30033 y : minY + (this.unitWidth + this.gutter) * 1
30037 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30038 y : minY + (this.unitWidth + this.gutter) * 2
30045 if(box[0].size == 'xs' && box[1].size == 'xs'){
30048 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30053 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30058 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30059 y : minY + (this.unitWidth + this.gutter) * 1
30067 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30072 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30073 y : minY + (this.unitWidth + this.gutter) * 2
30077 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30078 y : minY + (this.unitWidth + this.gutter) * 2
30085 getHorizontalFourBoxColPositions : function(maxX, minY, box)
30089 if(box[0].size == 'xs'){
30092 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30097 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30102 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),
30107 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30108 y : minY + (this.unitWidth + this.gutter) * 1
30116 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30121 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30122 y : minY + (this.unitWidth + this.gutter) * 2
30126 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30127 y : minY + (this.unitWidth + this.gutter) * 2
30131 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),
30132 y : minY + (this.unitWidth + this.gutter) * 2
30146 * http://masonry.desandro.com
30148 * The idea is to render all the bricks based on vertical width...
30150 * The original code extends 'outlayer' - we might need to use that....
30156 * @class Roo.bootstrap.LayoutMasonryAuto
30157 * @extends Roo.bootstrap.Component
30158 * Bootstrap Layout Masonry class
30161 * Create a new Element
30162 * @param {Object} config The config object
30165 Roo.bootstrap.LayoutMasonryAuto = function(config){
30166 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30169 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
30172 * @cfg {Boolean} isFitWidth - resize the width..
30174 isFitWidth : false, // options..
30176 * @cfg {Boolean} isOriginLeft = left align?
30178 isOriginLeft : true,
30180 * @cfg {Boolean} isOriginTop = top align?
30182 isOriginTop : false,
30184 * @cfg {Boolean} isLayoutInstant = no animation?
30186 isLayoutInstant : false, // needed?
30188 * @cfg {Boolean} isResizingContainer = not sure if this is used..
30190 isResizingContainer : true,
30192 * @cfg {Number} columnWidth width of the columns
30198 * @cfg {Number} maxCols maximum number of columns
30203 * @cfg {Number} padHeight padding below box..
30209 * @cfg {Boolean} isAutoInitial defalut true
30212 isAutoInitial : true,
30218 initialColumnWidth : 0,
30219 currentSize : null,
30221 colYs : null, // array.
30228 bricks: null, //CompositeElement
30229 cols : 0, // array?
30230 // element : null, // wrapped now this.el
30231 _isLayoutInited : null,
30234 getAutoCreate : function(){
30238 cls: 'blog-masonary-wrapper ' + this.cls,
30240 cls : 'mas-boxes masonary'
30247 getChildContainer: function( )
30249 if (this.boxesEl) {
30250 return this.boxesEl;
30253 this.boxesEl = this.el.select('.mas-boxes').first();
30255 return this.boxesEl;
30259 initEvents : function()
30263 if(this.isAutoInitial){
30264 Roo.log('hook children rendered');
30265 this.on('childrenrendered', function() {
30266 Roo.log('children rendered');
30273 initial : function()
30275 this.reloadItems();
30277 this.currentSize = this.el.getBox(true);
30279 /// was window resize... - let's see if this works..
30280 Roo.EventManager.onWindowResize(this.resize, this);
30282 if(!this.isAutoInitial){
30287 this.layout.defer(500,this);
30290 reloadItems: function()
30292 this.bricks = this.el.select('.masonry-brick', true);
30294 this.bricks.each(function(b) {
30295 //Roo.log(b.getSize());
30296 if (!b.attr('originalwidth')) {
30297 b.attr('originalwidth', b.getSize().width);
30302 Roo.log(this.bricks.elements.length);
30305 resize : function()
30308 var cs = this.el.getBox(true);
30310 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30311 Roo.log("no change in with or X");
30314 this.currentSize = cs;
30318 layout : function()
30321 this._resetLayout();
30322 //this._manageStamps();
30324 // don't animate first layout
30325 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30326 this.layoutItems( isInstant );
30328 // flag for initalized
30329 this._isLayoutInited = true;
30332 layoutItems : function( isInstant )
30334 //var items = this._getItemsForLayout( this.items );
30335 // original code supports filtering layout items.. we just ignore it..
30337 this._layoutItems( this.bricks , isInstant );
30339 this._postLayout();
30341 _layoutItems : function ( items , isInstant)
30343 //this.fireEvent( 'layout', this, items );
30346 if ( !items || !items.elements.length ) {
30347 // no items, emit event with empty array
30352 items.each(function(item) {
30353 Roo.log("layout item");
30355 // get x/y object from method
30356 var position = this._getItemLayoutPosition( item );
30358 position.item = item;
30359 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30360 queue.push( position );
30363 this._processLayoutQueue( queue );
30365 /** Sets position of item in DOM
30366 * @param {Element} item
30367 * @param {Number} x - horizontal position
30368 * @param {Number} y - vertical position
30369 * @param {Boolean} isInstant - disables transitions
30371 _processLayoutQueue : function( queue )
30373 for ( var i=0, len = queue.length; i < len; i++ ) {
30374 var obj = queue[i];
30375 obj.item.position('absolute');
30376 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30382 * Any logic you want to do after each layout,
30383 * i.e. size the container
30385 _postLayout : function()
30387 this.resizeContainer();
30390 resizeContainer : function()
30392 if ( !this.isResizingContainer ) {
30395 var size = this._getContainerSize();
30397 this.el.setSize(size.width,size.height);
30398 this.boxesEl.setSize(size.width,size.height);
30404 _resetLayout : function()
30406 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30407 this.colWidth = this.el.getWidth();
30408 //this.gutter = this.el.getWidth();
30410 this.measureColumns();
30416 this.colYs.push( 0 );
30422 measureColumns : function()
30424 this.getContainerWidth();
30425 // if columnWidth is 0, default to outerWidth of first item
30426 if ( !this.columnWidth ) {
30427 var firstItem = this.bricks.first();
30428 Roo.log(firstItem);
30429 this.columnWidth = this.containerWidth;
30430 if (firstItem && firstItem.attr('originalwidth') ) {
30431 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30433 // columnWidth fall back to item of first element
30434 Roo.log("set column width?");
30435 this.initialColumnWidth = this.columnWidth ;
30437 // if first elem has no width, default to size of container
30442 if (this.initialColumnWidth) {
30443 this.columnWidth = this.initialColumnWidth;
30448 // column width is fixed at the top - however if container width get's smaller we should
30451 // this bit calcs how man columns..
30453 var columnWidth = this.columnWidth += this.gutter;
30455 // calculate columns
30456 var containerWidth = this.containerWidth + this.gutter;
30458 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30459 // fix rounding errors, typically with gutters
30460 var excess = columnWidth - containerWidth % columnWidth;
30463 // if overshoot is less than a pixel, round up, otherwise floor it
30464 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30465 cols = Math[ mathMethod ]( cols );
30466 this.cols = Math.max( cols, 1 );
30467 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30469 // padding positioning..
30470 var totalColWidth = this.cols * this.columnWidth;
30471 var padavail = this.containerWidth - totalColWidth;
30472 // so for 2 columns - we need 3 'pads'
30474 var padNeeded = (1+this.cols) * this.padWidth;
30476 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30478 this.columnWidth += padExtra
30479 //this.padWidth = Math.floor(padavail / ( this.cols));
30481 // adjust colum width so that padding is fixed??
30483 // we have 3 columns ... total = width * 3
30484 // we have X left over... that should be used by
30486 //if (this.expandC) {
30494 getContainerWidth : function()
30496 /* // container is parent if fit width
30497 var container = this.isFitWidth ? this.element.parentNode : this.element;
30498 // check that this.size and size are there
30499 // IE8 triggers resize on body size change, so they might not be
30501 var size = getSize( container ); //FIXME
30502 this.containerWidth = size && size.innerWidth; //FIXME
30505 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30509 _getItemLayoutPosition : function( item ) // what is item?
30511 // we resize the item to our columnWidth..
30513 item.setWidth(this.columnWidth);
30514 item.autoBoxAdjust = false;
30516 var sz = item.getSize();
30518 // how many columns does this brick span
30519 var remainder = this.containerWidth % this.columnWidth;
30521 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30522 // round if off by 1 pixel, otherwise use ceil
30523 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30524 colSpan = Math.min( colSpan, this.cols );
30526 // normally this should be '1' as we dont' currently allow multi width columns..
30528 var colGroup = this._getColGroup( colSpan );
30529 // get the minimum Y value from the columns
30530 var minimumY = Math.min.apply( Math, colGroup );
30531 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30533 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30535 // position the brick
30537 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30538 y: this.currentSize.y + minimumY + this.padHeight
30542 // apply setHeight to necessary columns
30543 var setHeight = minimumY + sz.height + this.padHeight;
30544 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30546 var setSpan = this.cols + 1 - colGroup.length;
30547 for ( var i = 0; i < setSpan; i++ ) {
30548 this.colYs[ shortColIndex + i ] = setHeight ;
30555 * @param {Number} colSpan - number of columns the element spans
30556 * @returns {Array} colGroup
30558 _getColGroup : function( colSpan )
30560 if ( colSpan < 2 ) {
30561 // if brick spans only one column, use all the column Ys
30566 // how many different places could this brick fit horizontally
30567 var groupCount = this.cols + 1 - colSpan;
30568 // for each group potential horizontal position
30569 for ( var i = 0; i < groupCount; i++ ) {
30570 // make an array of colY values for that one group
30571 var groupColYs = this.colYs.slice( i, i + colSpan );
30572 // and get the max value of the array
30573 colGroup[i] = Math.max.apply( Math, groupColYs );
30578 _manageStamp : function( stamp )
30580 var stampSize = stamp.getSize();
30581 var offset = stamp.getBox();
30582 // get the columns that this stamp affects
30583 var firstX = this.isOriginLeft ? offset.x : offset.right;
30584 var lastX = firstX + stampSize.width;
30585 var firstCol = Math.floor( firstX / this.columnWidth );
30586 firstCol = Math.max( 0, firstCol );
30588 var lastCol = Math.floor( lastX / this.columnWidth );
30589 // lastCol should not go over if multiple of columnWidth #425
30590 lastCol -= lastX % this.columnWidth ? 0 : 1;
30591 lastCol = Math.min( this.cols - 1, lastCol );
30593 // set colYs to bottom of the stamp
30594 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30597 for ( var i = firstCol; i <= lastCol; i++ ) {
30598 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30603 _getContainerSize : function()
30605 this.maxY = Math.max.apply( Math, this.colYs );
30610 if ( this.isFitWidth ) {
30611 size.width = this._getContainerFitWidth();
30617 _getContainerFitWidth : function()
30619 var unusedCols = 0;
30620 // count unused columns
30623 if ( this.colYs[i] !== 0 ) {
30628 // fit container to columns that have been used
30629 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30632 needsResizeLayout : function()
30634 var previousWidth = this.containerWidth;
30635 this.getContainerWidth();
30636 return previousWidth !== this.containerWidth;
30651 * @class Roo.bootstrap.MasonryBrick
30652 * @extends Roo.bootstrap.Component
30653 * Bootstrap MasonryBrick class
30656 * Create a new MasonryBrick
30657 * @param {Object} config The config object
30660 Roo.bootstrap.MasonryBrick = function(config){
30661 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30667 * When a MasonryBrick is clcik
30668 * @param {Roo.bootstrap.MasonryBrick} this
30669 * @param {Roo.EventObject} e
30675 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
30678 * @cfg {String} title
30682 * @cfg {String} html
30686 * @cfg {String} bgimage
30690 * @cfg {String} videourl
30694 * @cfg {String} cls
30698 * @cfg {String} href
30702 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30707 * @cfg {String} (center|bottom) placetitle
30711 getAutoCreate : function()
30713 var cls = 'masonry-brick';
30715 if(this.href.length){
30716 cls += ' masonry-brick-link';
30719 if(this.bgimage.length){
30720 cls += ' masonry-brick-image';
30724 cls += ' masonry-' + this.size + '-brick';
30727 if(this.placetitle.length){
30729 switch (this.placetitle) {
30731 cls += ' masonry-center-title';
30734 cls += ' masonry-bottom-title';
30741 if(!this.html.length && !this.bgimage.length){
30742 cls += ' masonry-center-title';
30745 if(!this.html.length && this.bgimage.length){
30746 cls += ' masonry-bottom-title';
30751 cls += ' ' + this.cls;
30755 tag: (this.href.length) ? 'a' : 'div',
30760 cls: 'masonry-brick-paragraph',
30766 if(this.href.length){
30767 cfg.href = this.href;
30770 var cn = cfg.cn[0].cn;
30772 if(this.title.length){
30775 cls: 'masonry-brick-title',
30780 if(this.html.length){
30783 cls: 'masonry-brick-text',
30787 if (!this.title.length && !this.html.length) {
30788 cfg.cn[0].cls += ' hide';
30791 if(this.bgimage.length){
30794 cls: 'masonry-brick-image-view',
30798 if(this.videourl.length){
30799 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30800 // youtube support only?
30803 cls: 'masonry-brick-image-view',
30806 allowfullscreen : true
30815 initEvents: function()
30817 switch (this.size) {
30819 // this.intSize = 1;
30824 // this.intSize = 2;
30831 // this.intSize = 3;
30836 // this.intSize = 3;
30841 // this.intSize = 3;
30846 // this.intSize = 3;
30858 this.el.on('touchstart', this.onTouchStart, this);
30859 this.el.on('touchmove', this.onTouchMove, this);
30860 this.el.on('touchend', this.onTouchEnd, this);
30861 this.el.on('contextmenu', this.onContextMenu, this);
30863 this.el.on('mouseenter' ,this.enter, this);
30864 this.el.on('mouseleave', this.leave, this);
30867 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30868 this.parent().bricks.push(this);
30873 onClick: function(e, el)
30879 var time = this.endTimer - this.startTimer;
30887 e.preventDefault();
30890 enter: function(e, el)
30892 e.preventDefault();
30894 if(this.bgimage.length && this.html.length){
30895 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30899 leave: function(e, el)
30901 e.preventDefault();
30903 if(this.bgimage.length && this.html.length){
30904 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30908 onTouchStart: function(e, el)
30910 // e.preventDefault();
30912 this.touchmoved = false;
30914 if(!this.bgimage.length || !this.html.length){
30918 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30920 this.timer = new Date().getTime();
30924 onTouchMove: function(e, el)
30926 this.touchmoved = true;
30929 onContextMenu : function(e,el)
30931 e.preventDefault();
30932 e.stopPropagation();
30936 onTouchEnd: function(e, el)
30938 // e.preventDefault();
30940 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30947 if(!this.bgimage.length || !this.html.length){
30949 if(this.href.length){
30950 window.location.href = this.href;
30956 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30958 window.location.href = this.href;
30973 * @class Roo.bootstrap.Brick
30974 * @extends Roo.bootstrap.Component
30975 * Bootstrap Brick class
30978 * Create a new Brick
30979 * @param {Object} config The config object
30982 Roo.bootstrap.Brick = function(config){
30983 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30989 * When a Brick is click
30990 * @param {Roo.bootstrap.Brick} this
30991 * @param {Roo.EventObject} e
30997 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
31000 * @cfg {String} title
31004 * @cfg {String} html
31008 * @cfg {String} bgimage
31012 * @cfg {String} cls
31016 * @cfg {String} href
31020 * @cfg {String} video
31024 * @cfg {Boolean} square
31028 getAutoCreate : function()
31030 var cls = 'roo-brick';
31032 if(this.href.length){
31033 cls += ' roo-brick-link';
31036 if(this.bgimage.length){
31037 cls += ' roo-brick-image';
31040 if(!this.html.length && !this.bgimage.length){
31041 cls += ' roo-brick-center-title';
31044 if(!this.html.length && this.bgimage.length){
31045 cls += ' roo-brick-bottom-title';
31049 cls += ' ' + this.cls;
31053 tag: (this.href.length) ? 'a' : 'div',
31058 cls: 'roo-brick-paragraph',
31064 if(this.href.length){
31065 cfg.href = this.href;
31068 var cn = cfg.cn[0].cn;
31070 if(this.title.length){
31073 cls: 'roo-brick-title',
31078 if(this.html.length){
31081 cls: 'roo-brick-text',
31088 if(this.bgimage.length){
31091 cls: 'roo-brick-image-view',
31099 initEvents: function()
31101 if(this.title.length || this.html.length){
31102 this.el.on('mouseenter' ,this.enter, this);
31103 this.el.on('mouseleave', this.leave, this);
31107 Roo.EventManager.onWindowResize(this.resize, this);
31112 resize : function()
31114 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31116 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31117 // paragraph.setHeight(paragraph.getWidth());
31119 if(this.bgimage.length){
31120 var image = this.el.select('.roo-brick-image-view', true).first();
31121 image.setWidth(paragraph.getWidth());
31122 image.setHeight(paragraph.getWidth());
31127 enter: function(e, el)
31129 e.preventDefault();
31131 if(this.bgimage.length){
31132 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31133 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31137 leave: function(e, el)
31139 e.preventDefault();
31141 if(this.bgimage.length){
31142 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31143 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31153 * Ext JS Library 1.1.1
31154 * Copyright(c) 2006-2007, Ext JS, LLC.
31156 * Originally Released Under LGPL - original licence link has changed is not relivant.
31159 * <script type="text/javascript">
31164 * @class Roo.bootstrap.SplitBar
31165 * @extends Roo.util.Observable
31166 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31170 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31171 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31172 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31173 split.minSize = 100;
31174 split.maxSize = 600;
31175 split.animate = true;
31176 split.on('moved', splitterMoved);
31179 * Create a new SplitBar
31180 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
31181 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
31182 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31183 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
31184 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31185 position of the SplitBar).
31187 Roo.bootstrap.SplitBar = function(cfg){
31192 // dragElement : elm
31193 // resizingElement: el,
31195 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31196 // placement : Roo.bootstrap.SplitBar.LEFT ,
31197 // existingProxy ???
31200 this.el = Roo.get(cfg.dragElement, true);
31201 this.el.dom.unselectable = "on";
31203 this.resizingEl = Roo.get(cfg.resizingElement, true);
31207 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31208 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31211 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31214 * The minimum size of the resizing element. (Defaults to 0)
31220 * The maximum size of the resizing element. (Defaults to 2000)
31223 this.maxSize = 2000;
31226 * Whether to animate the transition to the new size
31229 this.animate = false;
31232 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31235 this.useShim = false;
31240 if(!cfg.existingProxy){
31242 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31244 this.proxy = Roo.get(cfg.existingProxy).dom;
31247 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31250 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31253 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31256 this.dragSpecs = {};
31259 * @private The adapter to use to positon and resize elements
31261 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31262 this.adapter.init(this);
31264 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31266 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31267 this.el.addClass("roo-splitbar-h");
31270 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31271 this.el.addClass("roo-splitbar-v");
31277 * Fires when the splitter is moved (alias for {@link #event-moved})
31278 * @param {Roo.bootstrap.SplitBar} this
31279 * @param {Number} newSize the new width or height
31284 * Fires when the splitter is moved
31285 * @param {Roo.bootstrap.SplitBar} this
31286 * @param {Number} newSize the new width or height
31290 * @event beforeresize
31291 * Fires before the splitter is dragged
31292 * @param {Roo.bootstrap.SplitBar} this
31294 "beforeresize" : true,
31296 "beforeapply" : true
31299 Roo.util.Observable.call(this);
31302 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31303 onStartProxyDrag : function(x, y){
31304 this.fireEvent("beforeresize", this);
31306 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
31308 o.enableDisplayMode("block");
31309 // all splitbars share the same overlay
31310 Roo.bootstrap.SplitBar.prototype.overlay = o;
31312 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31313 this.overlay.show();
31314 Roo.get(this.proxy).setDisplayed("block");
31315 var size = this.adapter.getElementSize(this);
31316 this.activeMinSize = this.getMinimumSize();;
31317 this.activeMaxSize = this.getMaximumSize();;
31318 var c1 = size - this.activeMinSize;
31319 var c2 = Math.max(this.activeMaxSize - size, 0);
31320 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31321 this.dd.resetConstraints();
31322 this.dd.setXConstraint(
31323 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
31324 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31326 this.dd.setYConstraint(0, 0);
31328 this.dd.resetConstraints();
31329 this.dd.setXConstraint(0, 0);
31330 this.dd.setYConstraint(
31331 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
31332 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31335 this.dragSpecs.startSize = size;
31336 this.dragSpecs.startPoint = [x, y];
31337 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31341 * @private Called after the drag operation by the DDProxy
31343 onEndProxyDrag : function(e){
31344 Roo.get(this.proxy).setDisplayed(false);
31345 var endPoint = Roo.lib.Event.getXY(e);
31347 this.overlay.hide();
31350 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31351 newSize = this.dragSpecs.startSize +
31352 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31353 endPoint[0] - this.dragSpecs.startPoint[0] :
31354 this.dragSpecs.startPoint[0] - endPoint[0]
31357 newSize = this.dragSpecs.startSize +
31358 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31359 endPoint[1] - this.dragSpecs.startPoint[1] :
31360 this.dragSpecs.startPoint[1] - endPoint[1]
31363 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31364 if(newSize != this.dragSpecs.startSize){
31365 if(this.fireEvent('beforeapply', this, newSize) !== false){
31366 this.adapter.setElementSize(this, newSize);
31367 this.fireEvent("moved", this, newSize);
31368 this.fireEvent("resize", this, newSize);
31374 * Get the adapter this SplitBar uses
31375 * @return The adapter object
31377 getAdapter : function(){
31378 return this.adapter;
31382 * Set the adapter this SplitBar uses
31383 * @param {Object} adapter A SplitBar adapter object
31385 setAdapter : function(adapter){
31386 this.adapter = adapter;
31387 this.adapter.init(this);
31391 * Gets the minimum size for the resizing element
31392 * @return {Number} The minimum size
31394 getMinimumSize : function(){
31395 return this.minSize;
31399 * Sets the minimum size for the resizing element
31400 * @param {Number} minSize The minimum size
31402 setMinimumSize : function(minSize){
31403 this.minSize = minSize;
31407 * Gets the maximum size for the resizing element
31408 * @return {Number} The maximum size
31410 getMaximumSize : function(){
31411 return this.maxSize;
31415 * Sets the maximum size for the resizing element
31416 * @param {Number} maxSize The maximum size
31418 setMaximumSize : function(maxSize){
31419 this.maxSize = maxSize;
31423 * Sets the initialize size for the resizing element
31424 * @param {Number} size The initial size
31426 setCurrentSize : function(size){
31427 var oldAnimate = this.animate;
31428 this.animate = false;
31429 this.adapter.setElementSize(this, size);
31430 this.animate = oldAnimate;
31434 * Destroy this splitbar.
31435 * @param {Boolean} removeEl True to remove the element
31437 destroy : function(removeEl){
31439 this.shim.remove();
31442 this.proxy.parentNode.removeChild(this.proxy);
31450 * @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.
31452 Roo.bootstrap.SplitBar.createProxy = function(dir){
31453 var proxy = new Roo.Element(document.createElement("div"));
31454 proxy.unselectable();
31455 var cls = 'roo-splitbar-proxy';
31456 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31457 document.body.appendChild(proxy.dom);
31462 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31463 * Default Adapter. It assumes the splitter and resizing element are not positioned
31464 * elements and only gets/sets the width of the element. Generally used for table based layouts.
31466 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31469 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31470 // do nothing for now
31471 init : function(s){
31475 * Called before drag operations to get the current size of the resizing element.
31476 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31478 getElementSize : function(s){
31479 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31480 return s.resizingEl.getWidth();
31482 return s.resizingEl.getHeight();
31487 * Called after drag operations to set the size of the resizing element.
31488 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31489 * @param {Number} newSize The new size to set
31490 * @param {Function} onComplete A function to be invoked when resizing is complete
31492 setElementSize : function(s, newSize, onComplete){
31493 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31495 s.resizingEl.setWidth(newSize);
31497 onComplete(s, newSize);
31500 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31505 s.resizingEl.setHeight(newSize);
31507 onComplete(s, newSize);
31510 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31517 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31518 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31519 * Adapter that moves the splitter element to align with the resized sizing element.
31520 * Used with an absolute positioned SplitBar.
31521 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31522 * document.body, make sure you assign an id to the body element.
31524 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31525 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31526 this.container = Roo.get(container);
31529 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31530 init : function(s){
31531 this.basic.init(s);
31534 getElementSize : function(s){
31535 return this.basic.getElementSize(s);
31538 setElementSize : function(s, newSize, onComplete){
31539 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31542 moveSplitter : function(s){
31543 var yes = Roo.bootstrap.SplitBar;
31544 switch(s.placement){
31546 s.el.setX(s.resizingEl.getRight());
31549 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31552 s.el.setY(s.resizingEl.getBottom());
31555 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31562 * Orientation constant - Create a vertical SplitBar
31566 Roo.bootstrap.SplitBar.VERTICAL = 1;
31569 * Orientation constant - Create a horizontal SplitBar
31573 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31576 * Placement constant - The resizing element is to the left of the splitter element
31580 Roo.bootstrap.SplitBar.LEFT = 1;
31583 * Placement constant - The resizing element is to the right of the splitter element
31587 Roo.bootstrap.SplitBar.RIGHT = 2;
31590 * Placement constant - The resizing element is positioned above the splitter element
31594 Roo.bootstrap.SplitBar.TOP = 3;
31597 * Placement constant - The resizing element is positioned under splitter element
31601 Roo.bootstrap.SplitBar.BOTTOM = 4;
31602 Roo.namespace("Roo.bootstrap.layout");/*
31604 * Ext JS Library 1.1.1
31605 * Copyright(c) 2006-2007, Ext JS, LLC.
31607 * Originally Released Under LGPL - original licence link has changed is not relivant.
31610 * <script type="text/javascript">
31614 * @class Roo.bootstrap.layout.Manager
31615 * @extends Roo.bootstrap.Component
31616 * Base class for layout managers.
31618 Roo.bootstrap.layout.Manager = function(config)
31620 Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31626 /** false to disable window resize monitoring @type Boolean */
31627 this.monitorWindowResize = true;
31632 * Fires when a layout is performed.
31633 * @param {Roo.LayoutManager} this
31637 * @event regionresized
31638 * Fires when the user resizes a region.
31639 * @param {Roo.LayoutRegion} region The resized region
31640 * @param {Number} newSize The new size (width for east/west, height for north/south)
31642 "regionresized" : true,
31644 * @event regioncollapsed
31645 * Fires when a region is collapsed.
31646 * @param {Roo.LayoutRegion} region The collapsed region
31648 "regioncollapsed" : true,
31650 * @event regionexpanded
31651 * Fires when a region is expanded.
31652 * @param {Roo.LayoutRegion} region The expanded region
31654 "regionexpanded" : true
31656 this.updating = false;
31659 this.el = Roo.get(config.el);
31665 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31670 monitorWindowResize : true,
31676 onRender : function(ct, position)
31679 this.el = Roo.get(ct);
31685 initEvents: function()
31689 // ie scrollbar fix
31690 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31691 document.body.scroll = "no";
31692 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31693 this.el.position('relative');
31695 this.id = this.el.id;
31696 this.el.addClass("roo-layout-container");
31697 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31698 if(this.el.dom != document.body ) {
31699 this.el.on('resize', this.layout,this);
31700 this.el.on('show', this.layout,this);
31706 * Returns true if this layout is currently being updated
31707 * @return {Boolean}
31709 isUpdating : function(){
31710 return this.updating;
31714 * Suspend the LayoutManager from doing auto-layouts while
31715 * making multiple add or remove calls
31717 beginUpdate : function(){
31718 this.updating = true;
31722 * Restore auto-layouts and optionally disable the manager from performing a layout
31723 * @param {Boolean} noLayout true to disable a layout update
31725 endUpdate : function(noLayout){
31726 this.updating = false;
31732 layout: function(){
31736 onRegionResized : function(region, newSize){
31737 this.fireEvent("regionresized", region, newSize);
31741 onRegionCollapsed : function(region){
31742 this.fireEvent("regioncollapsed", region);
31745 onRegionExpanded : function(region){
31746 this.fireEvent("regionexpanded", region);
31750 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31751 * performs box-model adjustments.
31752 * @return {Object} The size as an object {width: (the width), height: (the height)}
31754 getViewSize : function()
31757 if(this.el.dom != document.body){
31758 size = this.el.getSize();
31760 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31762 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31763 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31768 * Returns the Element this layout is bound to.
31769 * @return {Roo.Element}
31771 getEl : function(){
31776 * Returns the specified region.
31777 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31778 * @return {Roo.LayoutRegion}
31780 getRegion : function(target){
31781 return this.regions[target.toLowerCase()];
31784 onWindowResize : function(){
31785 if(this.monitorWindowResize){
31791 * Ext JS Library 1.1.1
31792 * Copyright(c) 2006-2007, Ext JS, LLC.
31794 * Originally Released Under LGPL - original licence link has changed is not relivant.
31797 * <script type="text/javascript">
31800 * @class Roo.bootstrap.layout.Border
31801 * @extends Roo.bootstrap.layout.Manager
31802 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31803 * please see: examples/bootstrap/nested.html<br><br>
31805 <b>The container the layout is rendered into can be either the body element or any other element.
31806 If it is not the body element, the container needs to either be an absolute positioned element,
31807 or you will need to add "position:relative" to the css of the container. You will also need to specify
31808 the container size if it is not the body element.</b>
31811 * Create a new Border
31812 * @param {Object} config Configuration options
31814 Roo.bootstrap.layout.Border = function(config){
31815 config = config || {};
31816 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31820 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31821 if(config[region]){
31822 config[region].region = region;
31823 this.addRegion(config[region]);
31829 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
31831 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31833 * Creates and adds a new region if it doesn't already exist.
31834 * @param {String} target The target region key (north, south, east, west or center).
31835 * @param {Object} config The regions config object
31836 * @return {BorderLayoutRegion} The new region
31838 addRegion : function(config)
31840 if(!this.regions[config.region]){
31841 var r = this.factory(config);
31842 this.bindRegion(r);
31844 return this.regions[config.region];
31848 bindRegion : function(r){
31849 this.regions[r.config.region] = r;
31851 r.on("visibilitychange", this.layout, this);
31852 r.on("paneladded", this.layout, this);
31853 r.on("panelremoved", this.layout, this);
31854 r.on("invalidated", this.layout, this);
31855 r.on("resized", this.onRegionResized, this);
31856 r.on("collapsed", this.onRegionCollapsed, this);
31857 r.on("expanded", this.onRegionExpanded, this);
31861 * Performs a layout update.
31863 layout : function()
31865 if(this.updating) {
31869 // render all the rebions if they have not been done alreayd?
31870 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31871 if(this.regions[region] && !this.regions[region].bodyEl){
31872 this.regions[region].onRender(this.el)
31876 var size = this.getViewSize();
31877 var w = size.width;
31878 var h = size.height;
31883 //var x = 0, y = 0;
31885 var rs = this.regions;
31886 var north = rs["north"];
31887 var south = rs["south"];
31888 var west = rs["west"];
31889 var east = rs["east"];
31890 var center = rs["center"];
31891 //if(this.hideOnLayout){ // not supported anymore
31892 //c.el.setStyle("display", "none");
31894 if(north && north.isVisible()){
31895 var b = north.getBox();
31896 var m = north.getMargins();
31897 b.width = w - (m.left+m.right);
31900 centerY = b.height + b.y + m.bottom;
31901 centerH -= centerY;
31902 north.updateBox(this.safeBox(b));
31904 if(south && south.isVisible()){
31905 var b = south.getBox();
31906 var m = south.getMargins();
31907 b.width = w - (m.left+m.right);
31909 var totalHeight = (b.height + m.top + m.bottom);
31910 b.y = h - totalHeight + m.top;
31911 centerH -= totalHeight;
31912 south.updateBox(this.safeBox(b));
31914 if(west && west.isVisible()){
31915 var b = west.getBox();
31916 var m = west.getMargins();
31917 b.height = centerH - (m.top+m.bottom);
31919 b.y = centerY + m.top;
31920 var totalWidth = (b.width + m.left + m.right);
31921 centerX += totalWidth;
31922 centerW -= totalWidth;
31923 west.updateBox(this.safeBox(b));
31925 if(east && east.isVisible()){
31926 var b = east.getBox();
31927 var m = east.getMargins();
31928 b.height = centerH - (m.top+m.bottom);
31929 var totalWidth = (b.width + m.left + m.right);
31930 b.x = w - totalWidth + m.left;
31931 b.y = centerY + m.top;
31932 centerW -= totalWidth;
31933 east.updateBox(this.safeBox(b));
31936 var m = center.getMargins();
31938 x: centerX + m.left,
31939 y: centerY + m.top,
31940 width: centerW - (m.left+m.right),
31941 height: centerH - (m.top+m.bottom)
31943 //if(this.hideOnLayout){
31944 //center.el.setStyle("display", "block");
31946 center.updateBox(this.safeBox(centerBox));
31949 this.fireEvent("layout", this);
31953 safeBox : function(box){
31954 box.width = Math.max(0, box.width);
31955 box.height = Math.max(0, box.height);
31960 * Adds a ContentPanel (or subclass) to this layout.
31961 * @param {String} target The target region key (north, south, east, west or center).
31962 * @param {Roo.ContentPanel} panel The panel to add
31963 * @return {Roo.ContentPanel} The added panel
31965 add : function(target, panel){
31967 target = target.toLowerCase();
31968 return this.regions[target].add(panel);
31972 * Remove a ContentPanel (or subclass) to this layout.
31973 * @param {String} target The target region key (north, south, east, west or center).
31974 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31975 * @return {Roo.ContentPanel} The removed panel
31977 remove : function(target, panel){
31978 target = target.toLowerCase();
31979 return this.regions[target].remove(panel);
31983 * Searches all regions for a panel with the specified id
31984 * @param {String} panelId
31985 * @return {Roo.ContentPanel} The panel or null if it wasn't found
31987 findPanel : function(panelId){
31988 var rs = this.regions;
31989 for(var target in rs){
31990 if(typeof rs[target] != "function"){
31991 var p = rs[target].getPanel(panelId);
32001 * Searches all regions for a panel with the specified id and activates (shows) it.
32002 * @param {String/ContentPanel} panelId The panels id or the panel itself
32003 * @return {Roo.ContentPanel} The shown panel or null
32005 showPanel : function(panelId) {
32006 var rs = this.regions;
32007 for(var target in rs){
32008 var r = rs[target];
32009 if(typeof r != "function"){
32010 if(r.hasPanel(panelId)){
32011 return r.showPanel(panelId);
32019 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32020 * @param {Roo.state.Provider} provider (optional) An alternate state provider
32023 restoreState : function(provider){
32025 provider = Roo.state.Manager;
32027 var sm = new Roo.LayoutStateManager();
32028 sm.init(this, provider);
32034 * Adds a xtype elements to the layout.
32038 xtype : 'ContentPanel',
32045 xtype : 'NestedLayoutPanel',
32051 items : [ ... list of content panels or nested layout panels.. ]
32055 * @param {Object} cfg Xtype definition of item to add.
32057 addxtype : function(cfg)
32059 // basically accepts a pannel...
32060 // can accept a layout region..!?!?
32061 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32064 // theory? children can only be panels??
32066 //if (!cfg.xtype.match(/Panel$/)) {
32071 if (typeof(cfg.region) == 'undefined') {
32072 Roo.log("Failed to add Panel, region was not set");
32076 var region = cfg.region;
32082 xitems = cfg.items;
32089 case 'Content': // ContentPanel (el, cfg)
32090 case 'Scroll': // ContentPanel (el, cfg)
32092 cfg.autoCreate = true;
32093 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32095 // var el = this.el.createChild();
32096 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32099 this.add(region, ret);
32103 case 'TreePanel': // our new panel!
32104 cfg.el = this.el.createChild();
32105 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32106 this.add(region, ret);
32111 // create a new Layout (which is a Border Layout...
32113 var clayout = cfg.layout;
32114 clayout.el = this.el.createChild();
32115 clayout.items = clayout.items || [];
32119 // replace this exitems with the clayout ones..
32120 xitems = clayout.items;
32122 // force background off if it's in center...
32123 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32124 cfg.background = false;
32126 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
32129 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32130 //console.log('adding nested layout panel ' + cfg.toSource());
32131 this.add(region, ret);
32132 nb = {}; /// find first...
32137 // needs grid and region
32139 //var el = this.getRegion(region).el.createChild();
32141 *var el = this.el.createChild();
32142 // create the grid first...
32143 cfg.grid.container = el;
32144 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
32147 if (region == 'center' && this.active ) {
32148 cfg.background = false;
32151 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32153 this.add(region, ret);
32155 if (cfg.background) {
32156 // render grid on panel activation (if panel background)
32157 ret.on('activate', function(gp) {
32158 if (!gp.grid.rendered) {
32159 // gp.grid.render(el);
32163 // cfg.grid.render(el);
32169 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
32170 // it was the old xcomponent building that caused this before.
32171 // espeically if border is the top element in the tree.
32181 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32183 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32184 this.add(region, ret);
32188 throw "Can not add '" + cfg.xtype + "' to Border";
32194 this.beginUpdate();
32198 Roo.each(xitems, function(i) {
32199 region = nb && i.region ? i.region : false;
32201 var add = ret.addxtype(i);
32204 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32205 if (!i.background) {
32206 abn[region] = nb[region] ;
32213 // make the last non-background panel active..
32214 //if (nb) { Roo.log(abn); }
32217 for(var r in abn) {
32218 region = this.getRegion(r);
32220 // tried using nb[r], but it does not work..
32222 region.showPanel(abn[r]);
32233 factory : function(cfg)
32236 var validRegions = Roo.bootstrap.layout.Border.regions;
32238 var target = cfg.region;
32241 var r = Roo.bootstrap.layout;
32245 return new r.North(cfg);
32247 return new r.South(cfg);
32249 return new r.East(cfg);
32251 return new r.West(cfg);
32253 return new r.Center(cfg);
32255 throw 'Layout region "'+target+'" not supported.';
32262 * Ext JS Library 1.1.1
32263 * Copyright(c) 2006-2007, Ext JS, LLC.
32265 * Originally Released Under LGPL - original licence link has changed is not relivant.
32268 * <script type="text/javascript">
32272 * @class Roo.bootstrap.layout.Basic
32273 * @extends Roo.util.Observable
32274 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32275 * and does not have a titlebar, tabs or any other features. All it does is size and position
32276 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32277 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32278 * @cfg {string} region the region that it inhabits..
32279 * @cfg {bool} skipConfig skip config?
32283 Roo.bootstrap.layout.Basic = function(config){
32285 this.mgr = config.mgr;
32287 this.position = config.region;
32289 var skipConfig = config.skipConfig;
32293 * @scope Roo.BasicLayoutRegion
32297 * @event beforeremove
32298 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32299 * @param {Roo.LayoutRegion} this
32300 * @param {Roo.ContentPanel} panel The panel
32301 * @param {Object} e The cancel event object
32303 "beforeremove" : true,
32305 * @event invalidated
32306 * Fires when the layout for this region is changed.
32307 * @param {Roo.LayoutRegion} this
32309 "invalidated" : true,
32311 * @event visibilitychange
32312 * Fires when this region is shown or hidden
32313 * @param {Roo.LayoutRegion} this
32314 * @param {Boolean} visibility true or false
32316 "visibilitychange" : true,
32318 * @event paneladded
32319 * Fires when a panel is added.
32320 * @param {Roo.LayoutRegion} this
32321 * @param {Roo.ContentPanel} panel The panel
32323 "paneladded" : true,
32325 * @event panelremoved
32326 * Fires when a panel is removed.
32327 * @param {Roo.LayoutRegion} this
32328 * @param {Roo.ContentPanel} panel The panel
32330 "panelremoved" : true,
32332 * @event beforecollapse
32333 * Fires when this region before collapse.
32334 * @param {Roo.LayoutRegion} this
32336 "beforecollapse" : true,
32339 * Fires when this region is collapsed.
32340 * @param {Roo.LayoutRegion} this
32342 "collapsed" : true,
32345 * Fires when this region is expanded.
32346 * @param {Roo.LayoutRegion} this
32351 * Fires when this region is slid into view.
32352 * @param {Roo.LayoutRegion} this
32354 "slideshow" : true,
32357 * Fires when this region slides out of view.
32358 * @param {Roo.LayoutRegion} this
32360 "slidehide" : true,
32362 * @event panelactivated
32363 * Fires when a panel is activated.
32364 * @param {Roo.LayoutRegion} this
32365 * @param {Roo.ContentPanel} panel The activated panel
32367 "panelactivated" : true,
32370 * Fires when the user resizes this region.
32371 * @param {Roo.LayoutRegion} this
32372 * @param {Number} newSize The new size (width for east/west, height for north/south)
32376 /** A collection of panels in this region. @type Roo.util.MixedCollection */
32377 this.panels = new Roo.util.MixedCollection();
32378 this.panels.getKey = this.getPanelId.createDelegate(this);
32380 this.activePanel = null;
32381 // ensure listeners are added...
32383 if (config.listeners || config.events) {
32384 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32385 listeners : config.listeners || {},
32386 events : config.events || {}
32390 if(skipConfig !== true){
32391 this.applyConfig(config);
32395 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32397 getPanelId : function(p){
32401 applyConfig : function(config){
32402 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32403 this.config = config;
32408 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
32409 * the width, for horizontal (north, south) the height.
32410 * @param {Number} newSize The new width or height
32412 resizeTo : function(newSize){
32413 var el = this.el ? this.el :
32414 (this.activePanel ? this.activePanel.getEl() : null);
32416 switch(this.position){
32419 el.setWidth(newSize);
32420 this.fireEvent("resized", this, newSize);
32424 el.setHeight(newSize);
32425 this.fireEvent("resized", this, newSize);
32431 getBox : function(){
32432 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32435 getMargins : function(){
32436 return this.margins;
32439 updateBox : function(box){
32441 var el = this.activePanel.getEl();
32442 el.dom.style.left = box.x + "px";
32443 el.dom.style.top = box.y + "px";
32444 this.activePanel.setSize(box.width, box.height);
32448 * Returns the container element for this region.
32449 * @return {Roo.Element}
32451 getEl : function(){
32452 return this.activePanel;
32456 * Returns true if this region is currently visible.
32457 * @return {Boolean}
32459 isVisible : function(){
32460 return this.activePanel ? true : false;
32463 setActivePanel : function(panel){
32464 panel = this.getPanel(panel);
32465 if(this.activePanel && this.activePanel != panel){
32466 this.activePanel.setActiveState(false);
32467 this.activePanel.getEl().setLeftTop(-10000,-10000);
32469 this.activePanel = panel;
32470 panel.setActiveState(true);
32472 panel.setSize(this.box.width, this.box.height);
32474 this.fireEvent("panelactivated", this, panel);
32475 this.fireEvent("invalidated");
32479 * Show the specified panel.
32480 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32481 * @return {Roo.ContentPanel} The shown panel or null
32483 showPanel : function(panel){
32484 panel = this.getPanel(panel);
32486 this.setActivePanel(panel);
32492 * Get the active panel for this region.
32493 * @return {Roo.ContentPanel} The active panel or null
32495 getActivePanel : function(){
32496 return this.activePanel;
32500 * Add the passed ContentPanel(s)
32501 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32502 * @return {Roo.ContentPanel} The panel added (if only one was added)
32504 add : function(panel){
32505 if(arguments.length > 1){
32506 for(var i = 0, len = arguments.length; i < len; i++) {
32507 this.add(arguments[i]);
32511 if(this.hasPanel(panel)){
32512 this.showPanel(panel);
32515 var el = panel.getEl();
32516 if(el.dom.parentNode != this.mgr.el.dom){
32517 this.mgr.el.dom.appendChild(el.dom);
32519 if(panel.setRegion){
32520 panel.setRegion(this);
32522 this.panels.add(panel);
32523 el.setStyle("position", "absolute");
32524 if(!panel.background){
32525 this.setActivePanel(panel);
32526 if(this.config.initialSize && this.panels.getCount()==1){
32527 this.resizeTo(this.config.initialSize);
32530 this.fireEvent("paneladded", this, panel);
32535 * Returns true if the panel is in this region.
32536 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32537 * @return {Boolean}
32539 hasPanel : function(panel){
32540 if(typeof panel == "object"){ // must be panel obj
32541 panel = panel.getId();
32543 return this.getPanel(panel) ? true : false;
32547 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32548 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32549 * @param {Boolean} preservePanel Overrides the config preservePanel option
32550 * @return {Roo.ContentPanel} The panel that was removed
32552 remove : function(panel, preservePanel){
32553 panel = this.getPanel(panel);
32558 this.fireEvent("beforeremove", this, panel, e);
32559 if(e.cancel === true){
32562 var panelId = panel.getId();
32563 this.panels.removeKey(panelId);
32568 * Returns the panel specified or null if it's not in this region.
32569 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32570 * @return {Roo.ContentPanel}
32572 getPanel : function(id){
32573 if(typeof id == "object"){ // must be panel obj
32576 return this.panels.get(id);
32580 * Returns this regions position (north/south/east/west/center).
32583 getPosition: function(){
32584 return this.position;
32588 * Ext JS Library 1.1.1
32589 * Copyright(c) 2006-2007, Ext JS, LLC.
32591 * Originally Released Under LGPL - original licence link has changed is not relivant.
32594 * <script type="text/javascript">
32598 * @class Roo.bootstrap.layout.Region
32599 * @extends Roo.bootstrap.layout.Basic
32600 * This class represents a region in a layout manager.
32602 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32603 * @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})
32604 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
32605 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
32606 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
32607 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
32608 * @cfg {String} title The title for the region (overrides panel titles)
32609 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
32610 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32611 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
32612 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32613 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
32614 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32615 * the space available, similar to FireFox 1.5 tabs (defaults to false)
32616 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
32617 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
32618 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
32620 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
32621 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
32622 * @cfg {Boolean} disableTabTips True to disable tab tooltips
32623 * @cfg {Number} width For East/West panels
32624 * @cfg {Number} height For North/South panels
32625 * @cfg {Boolean} split To show the splitter
32626 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
32628 * @cfg {string} cls Extra CSS classes to add to region
32630 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32631 * @cfg {string} region the region that it inhabits..
32634 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
32635 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
32637 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
32638 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
32639 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
32641 Roo.bootstrap.layout.Region = function(config)
32643 this.applyConfig(config);
32645 var mgr = config.mgr;
32646 var pos = config.region;
32647 config.skipConfig = true;
32648 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32651 this.onRender(mgr.el);
32654 this.visible = true;
32655 this.collapsed = false;
32656 this.unrendered_panels = [];
32659 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32661 position: '', // set by wrapper (eg. north/south etc..)
32662 unrendered_panels : null, // unrendered panels.
32663 createBody : function(){
32664 /** This region's body element
32665 * @type Roo.Element */
32666 this.bodyEl = this.el.createChild({
32668 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32672 onRender: function(ctr, pos)
32674 var dh = Roo.DomHelper;
32675 /** This region's container element
32676 * @type Roo.Element */
32677 this.el = dh.append(ctr.dom, {
32679 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32681 /** This region's title element
32682 * @type Roo.Element */
32684 this.titleEl = dh.append(this.el.dom,
32687 unselectable: "on",
32688 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32690 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
32691 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32694 this.titleEl.enableDisplayMode();
32695 /** This region's title text element
32696 * @type HTMLElement */
32697 this.titleTextEl = this.titleEl.dom.firstChild;
32698 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32700 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32701 this.closeBtn.enableDisplayMode();
32702 this.closeBtn.on("click", this.closeClicked, this);
32703 this.closeBtn.hide();
32705 this.createBody(this.config);
32706 if(this.config.hideWhenEmpty){
32708 this.on("paneladded", this.validateVisibility, this);
32709 this.on("panelremoved", this.validateVisibility, this);
32711 if(this.autoScroll){
32712 this.bodyEl.setStyle("overflow", "auto");
32714 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32716 //if(c.titlebar !== false){
32717 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32718 this.titleEl.hide();
32720 this.titleEl.show();
32721 if(this.config.title){
32722 this.titleTextEl.innerHTML = this.config.title;
32726 if(this.config.collapsed){
32727 this.collapse(true);
32729 if(this.config.hidden){
32733 if (this.unrendered_panels && this.unrendered_panels.length) {
32734 for (var i =0;i< this.unrendered_panels.length; i++) {
32735 this.add(this.unrendered_panels[i]);
32737 this.unrendered_panels = null;
32743 applyConfig : function(c)
32746 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32747 var dh = Roo.DomHelper;
32748 if(c.titlebar !== false){
32749 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32750 this.collapseBtn.on("click", this.collapse, this);
32751 this.collapseBtn.enableDisplayMode();
32753 if(c.showPin === true || this.showPin){
32754 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32755 this.stickBtn.enableDisplayMode();
32756 this.stickBtn.on("click", this.expand, this);
32757 this.stickBtn.hide();
32762 /** This region's collapsed element
32763 * @type Roo.Element */
32766 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32767 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32770 if(c.floatable !== false){
32771 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32772 this.collapsedEl.on("click", this.collapseClick, this);
32775 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32776 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32777 id: "message", unselectable: "on", style:{"float":"left"}});
32778 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32780 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32781 this.expandBtn.on("click", this.expand, this);
32785 if(this.collapseBtn){
32786 this.collapseBtn.setVisible(c.collapsible == true);
32789 this.cmargins = c.cmargins || this.cmargins ||
32790 (this.position == "west" || this.position == "east" ?
32791 {top: 0, left: 2, right:2, bottom: 0} :
32792 {top: 2, left: 0, right:0, bottom: 2});
32794 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32797 this.bottomTabs = c.tabPosition != "top";
32799 this.autoScroll = c.autoScroll || false;
32804 this.duration = c.duration || .30;
32805 this.slideDuration = c.slideDuration || .45;
32810 * Returns true if this region is currently visible.
32811 * @return {Boolean}
32813 isVisible : function(){
32814 return this.visible;
32818 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32819 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
32821 //setCollapsedTitle : function(title){
32822 // title = title || " ";
32823 // if(this.collapsedTitleTextEl){
32824 // this.collapsedTitleTextEl.innerHTML = title;
32828 getBox : function(){
32830 // if(!this.collapsed){
32831 b = this.el.getBox(false, true);
32833 // b = this.collapsedEl.getBox(false, true);
32838 getMargins : function(){
32839 return this.margins;
32840 //return this.collapsed ? this.cmargins : this.margins;
32843 highlight : function(){
32844 this.el.addClass("x-layout-panel-dragover");
32847 unhighlight : function(){
32848 this.el.removeClass("x-layout-panel-dragover");
32851 updateBox : function(box)
32853 if (!this.bodyEl) {
32854 return; // not rendered yet..
32858 if(!this.collapsed){
32859 this.el.dom.style.left = box.x + "px";
32860 this.el.dom.style.top = box.y + "px";
32861 this.updateBody(box.width, box.height);
32863 this.collapsedEl.dom.style.left = box.x + "px";
32864 this.collapsedEl.dom.style.top = box.y + "px";
32865 this.collapsedEl.setSize(box.width, box.height);
32868 this.tabs.autoSizeTabs();
32872 updateBody : function(w, h)
32875 this.el.setWidth(w);
32876 w -= this.el.getBorderWidth("rl");
32877 if(this.config.adjustments){
32878 w += this.config.adjustments[0];
32881 if(h !== null && h > 0){
32882 this.el.setHeight(h);
32883 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32884 h -= this.el.getBorderWidth("tb");
32885 if(this.config.adjustments){
32886 h += this.config.adjustments[1];
32888 this.bodyEl.setHeight(h);
32890 h = this.tabs.syncHeight(h);
32893 if(this.panelSize){
32894 w = w !== null ? w : this.panelSize.width;
32895 h = h !== null ? h : this.panelSize.height;
32897 if(this.activePanel){
32898 var el = this.activePanel.getEl();
32899 w = w !== null ? w : el.getWidth();
32900 h = h !== null ? h : el.getHeight();
32901 this.panelSize = {width: w, height: h};
32902 this.activePanel.setSize(w, h);
32904 if(Roo.isIE && this.tabs){
32905 this.tabs.el.repaint();
32910 * Returns the container element for this region.
32911 * @return {Roo.Element}
32913 getEl : function(){
32918 * Hides this region.
32921 //if(!this.collapsed){
32922 this.el.dom.style.left = "-2000px";
32925 // this.collapsedEl.dom.style.left = "-2000px";
32926 // this.collapsedEl.hide();
32928 this.visible = false;
32929 this.fireEvent("visibilitychange", this, false);
32933 * Shows this region if it was previously hidden.
32936 //if(!this.collapsed){
32939 // this.collapsedEl.show();
32941 this.visible = true;
32942 this.fireEvent("visibilitychange", this, true);
32945 closeClicked : function(){
32946 if(this.activePanel){
32947 this.remove(this.activePanel);
32951 collapseClick : function(e){
32953 e.stopPropagation();
32956 e.stopPropagation();
32962 * Collapses this region.
32963 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32966 collapse : function(skipAnim, skipCheck = false){
32967 if(this.collapsed) {
32971 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32973 this.collapsed = true;
32975 this.split.el.hide();
32977 if(this.config.animate && skipAnim !== true){
32978 this.fireEvent("invalidated", this);
32979 this.animateCollapse();
32981 this.el.setLocation(-20000,-20000);
32983 this.collapsedEl.show();
32984 this.fireEvent("collapsed", this);
32985 this.fireEvent("invalidated", this);
32991 animateCollapse : function(){
32996 * Expands this region if it was previously collapsed.
32997 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32998 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33001 expand : function(e, skipAnim){
33003 e.stopPropagation();
33005 if(!this.collapsed || this.el.hasActiveFx()) {
33009 this.afterSlideIn();
33012 this.collapsed = false;
33013 if(this.config.animate && skipAnim !== true){
33014 this.animateExpand();
33018 this.split.el.show();
33020 this.collapsedEl.setLocation(-2000,-2000);
33021 this.collapsedEl.hide();
33022 this.fireEvent("invalidated", this);
33023 this.fireEvent("expanded", this);
33027 animateExpand : function(){
33031 initTabs : function()
33033 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33035 var ts = new Roo.bootstrap.panel.Tabs({
33036 el: this.bodyEl.dom,
33037 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33038 disableTooltips: this.config.disableTabTips,
33039 toolbar : this.config.toolbar
33042 if(this.config.hideTabs){
33043 ts.stripWrap.setDisplayed(false);
33046 ts.resizeTabs = this.config.resizeTabs === true;
33047 ts.minTabWidth = this.config.minTabWidth || 40;
33048 ts.maxTabWidth = this.config.maxTabWidth || 250;
33049 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33050 ts.monitorResize = false;
33051 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33052 ts.bodyEl.addClass('roo-layout-tabs-body');
33053 this.panels.each(this.initPanelAsTab, this);
33056 initPanelAsTab : function(panel){
33057 var ti = this.tabs.addTab(
33061 this.config.closeOnTab && panel.isClosable()
33063 if(panel.tabTip !== undefined){
33064 ti.setTooltip(panel.tabTip);
33066 ti.on("activate", function(){
33067 this.setActivePanel(panel);
33070 if(this.config.closeOnTab){
33071 ti.on("beforeclose", function(t, e){
33073 this.remove(panel);
33079 updatePanelTitle : function(panel, title)
33081 if(this.activePanel == panel){
33082 this.updateTitle(title);
33085 var ti = this.tabs.getTab(panel.getEl().id);
33087 if(panel.tabTip !== undefined){
33088 ti.setTooltip(panel.tabTip);
33093 updateTitle : function(title){
33094 if(this.titleTextEl && !this.config.title){
33095 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
33099 setActivePanel : function(panel)
33101 panel = this.getPanel(panel);
33102 if(this.activePanel && this.activePanel != panel){
33103 this.activePanel.setActiveState(false);
33105 this.activePanel = panel;
33106 panel.setActiveState(true);
33107 if(this.panelSize){
33108 panel.setSize(this.panelSize.width, this.panelSize.height);
33111 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33113 this.updateTitle(panel.getTitle());
33115 this.fireEvent("invalidated", this);
33117 this.fireEvent("panelactivated", this, panel);
33121 * Shows the specified panel.
33122 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33123 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33125 showPanel : function(panel)
33127 panel = this.getPanel(panel);
33130 var tab = this.tabs.getTab(panel.getEl().id);
33131 if(tab.isHidden()){
33132 this.tabs.unhideTab(tab.id);
33136 this.setActivePanel(panel);
33143 * Get the active panel for this region.
33144 * @return {Roo.ContentPanel} The active panel or null
33146 getActivePanel : function(){
33147 return this.activePanel;
33150 validateVisibility : function(){
33151 if(this.panels.getCount() < 1){
33152 this.updateTitle(" ");
33153 this.closeBtn.hide();
33156 if(!this.isVisible()){
33163 * Adds the passed ContentPanel(s) to this region.
33164 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33165 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33167 add : function(panel)
33169 if(arguments.length > 1){
33170 for(var i = 0, len = arguments.length; i < len; i++) {
33171 this.add(arguments[i]);
33176 // if we have not been rendered yet, then we can not really do much of this..
33177 if (!this.bodyEl) {
33178 this.unrendered_panels.push(panel);
33185 if(this.hasPanel(panel)){
33186 this.showPanel(panel);
33189 panel.setRegion(this);
33190 this.panels.add(panel);
33191 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33192 // sinle panel - no tab...?? would it not be better to render it with the tabs,
33193 // and hide them... ???
33194 this.bodyEl.dom.appendChild(panel.getEl().dom);
33195 if(panel.background !== true){
33196 this.setActivePanel(panel);
33198 this.fireEvent("paneladded", this, panel);
33205 this.initPanelAsTab(panel);
33209 if(panel.background !== true){
33210 this.tabs.activate(panel.getEl().id);
33212 this.fireEvent("paneladded", this, panel);
33217 * Hides the tab for the specified panel.
33218 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33220 hidePanel : function(panel){
33221 if(this.tabs && (panel = this.getPanel(panel))){
33222 this.tabs.hideTab(panel.getEl().id);
33227 * Unhides the tab for a previously hidden panel.
33228 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33230 unhidePanel : function(panel){
33231 if(this.tabs && (panel = this.getPanel(panel))){
33232 this.tabs.unhideTab(panel.getEl().id);
33236 clearPanels : function(){
33237 while(this.panels.getCount() > 0){
33238 this.remove(this.panels.first());
33243 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33244 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33245 * @param {Boolean} preservePanel Overrides the config preservePanel option
33246 * @return {Roo.ContentPanel} The panel that was removed
33248 remove : function(panel, preservePanel)
33250 panel = this.getPanel(panel);
33255 this.fireEvent("beforeremove", this, panel, e);
33256 if(e.cancel === true){
33259 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33260 var panelId = panel.getId();
33261 this.panels.removeKey(panelId);
33263 document.body.appendChild(panel.getEl().dom);
33266 this.tabs.removeTab(panel.getEl().id);
33267 }else if (!preservePanel){
33268 this.bodyEl.dom.removeChild(panel.getEl().dom);
33270 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33271 var p = this.panels.first();
33272 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33273 tempEl.appendChild(p.getEl().dom);
33274 this.bodyEl.update("");
33275 this.bodyEl.dom.appendChild(p.getEl().dom);
33277 this.updateTitle(p.getTitle());
33279 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33280 this.setActivePanel(p);
33282 panel.setRegion(null);
33283 if(this.activePanel == panel){
33284 this.activePanel = null;
33286 if(this.config.autoDestroy !== false && preservePanel !== true){
33287 try{panel.destroy();}catch(e){}
33289 this.fireEvent("panelremoved", this, panel);
33294 * Returns the TabPanel component used by this region
33295 * @return {Roo.TabPanel}
33297 getTabs : function(){
33301 createTool : function(parentEl, className){
33302 var btn = Roo.DomHelper.append(parentEl, {
33304 cls: "x-layout-tools-button",
33307 cls: "roo-layout-tools-button-inner " + className,
33311 btn.addClassOnOver("roo-layout-tools-button-over");
33316 * Ext JS Library 1.1.1
33317 * Copyright(c) 2006-2007, Ext JS, LLC.
33319 * Originally Released Under LGPL - original licence link has changed is not relivant.
33322 * <script type="text/javascript">
33328 * @class Roo.SplitLayoutRegion
33329 * @extends Roo.LayoutRegion
33330 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33332 Roo.bootstrap.layout.Split = function(config){
33333 this.cursor = config.cursor;
33334 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33337 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33339 splitTip : "Drag to resize.",
33340 collapsibleSplitTip : "Drag to resize. Double click to hide.",
33341 useSplitTips : false,
33343 applyConfig : function(config){
33344 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33347 onRender : function(ctr,pos) {
33349 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33350 if(!this.config.split){
33355 var splitEl = Roo.DomHelper.append(ctr.dom, {
33357 id: this.el.id + "-split",
33358 cls: "roo-layout-split roo-layout-split-"+this.position,
33361 /** The SplitBar for this region
33362 * @type Roo.SplitBar */
33363 // does not exist yet...
33364 Roo.log([this.position, this.orientation]);
33366 this.split = new Roo.bootstrap.SplitBar({
33367 dragElement : splitEl,
33368 resizingElement: this.el,
33369 orientation : this.orientation
33372 this.split.on("moved", this.onSplitMove, this);
33373 this.split.useShim = this.config.useShim === true;
33374 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33375 if(this.useSplitTips){
33376 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33378 //if(config.collapsible){
33379 // this.split.el.on("dblclick", this.collapse, this);
33382 if(typeof this.config.minSize != "undefined"){
33383 this.split.minSize = this.config.minSize;
33385 if(typeof this.config.maxSize != "undefined"){
33386 this.split.maxSize = this.config.maxSize;
33388 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33389 this.hideSplitter();
33394 getHMaxSize : function(){
33395 var cmax = this.config.maxSize || 10000;
33396 var center = this.mgr.getRegion("center");
33397 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33400 getVMaxSize : function(){
33401 var cmax = this.config.maxSize || 10000;
33402 var center = this.mgr.getRegion("center");
33403 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33406 onSplitMove : function(split, newSize){
33407 this.fireEvent("resized", this, newSize);
33411 * Returns the {@link Roo.SplitBar} for this region.
33412 * @return {Roo.SplitBar}
33414 getSplitBar : function(){
33419 this.hideSplitter();
33420 Roo.bootstrap.layout.Split.superclass.hide.call(this);
33423 hideSplitter : function(){
33425 this.split.el.setLocation(-2000,-2000);
33426 this.split.el.hide();
33432 this.split.el.show();
33434 Roo.bootstrap.layout.Split.superclass.show.call(this);
33437 beforeSlide: function(){
33438 if(Roo.isGecko){// firefox overflow auto bug workaround
33439 this.bodyEl.clip();
33441 this.tabs.bodyEl.clip();
33443 if(this.activePanel){
33444 this.activePanel.getEl().clip();
33446 if(this.activePanel.beforeSlide){
33447 this.activePanel.beforeSlide();
33453 afterSlide : function(){
33454 if(Roo.isGecko){// firefox overflow auto bug workaround
33455 this.bodyEl.unclip();
33457 this.tabs.bodyEl.unclip();
33459 if(this.activePanel){
33460 this.activePanel.getEl().unclip();
33461 if(this.activePanel.afterSlide){
33462 this.activePanel.afterSlide();
33468 initAutoHide : function(){
33469 if(this.autoHide !== false){
33470 if(!this.autoHideHd){
33471 var st = new Roo.util.DelayedTask(this.slideIn, this);
33472 this.autoHideHd = {
33473 "mouseout": function(e){
33474 if(!e.within(this.el, true)){
33478 "mouseover" : function(e){
33484 this.el.on(this.autoHideHd);
33488 clearAutoHide : function(){
33489 if(this.autoHide !== false){
33490 this.el.un("mouseout", this.autoHideHd.mouseout);
33491 this.el.un("mouseover", this.autoHideHd.mouseover);
33495 clearMonitor : function(){
33496 Roo.get(document).un("click", this.slideInIf, this);
33499 // these names are backwards but not changed for compat
33500 slideOut : function(){
33501 if(this.isSlid || this.el.hasActiveFx()){
33504 this.isSlid = true;
33505 if(this.collapseBtn){
33506 this.collapseBtn.hide();
33508 this.closeBtnState = this.closeBtn.getStyle('display');
33509 this.closeBtn.hide();
33511 this.stickBtn.show();
33514 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33515 this.beforeSlide();
33516 this.el.setStyle("z-index", 10001);
33517 this.el.slideIn(this.getSlideAnchor(), {
33518 callback: function(){
33520 this.initAutoHide();
33521 Roo.get(document).on("click", this.slideInIf, this);
33522 this.fireEvent("slideshow", this);
33529 afterSlideIn : function(){
33530 this.clearAutoHide();
33531 this.isSlid = false;
33532 this.clearMonitor();
33533 this.el.setStyle("z-index", "");
33534 if(this.collapseBtn){
33535 this.collapseBtn.show();
33537 this.closeBtn.setStyle('display', this.closeBtnState);
33539 this.stickBtn.hide();
33541 this.fireEvent("slidehide", this);
33544 slideIn : function(cb){
33545 if(!this.isSlid || this.el.hasActiveFx()){
33549 this.isSlid = false;
33550 this.beforeSlide();
33551 this.el.slideOut(this.getSlideAnchor(), {
33552 callback: function(){
33553 this.el.setLeftTop(-10000, -10000);
33555 this.afterSlideIn();
33563 slideInIf : function(e){
33564 if(!e.within(this.el)){
33569 animateCollapse : function(){
33570 this.beforeSlide();
33571 this.el.setStyle("z-index", 20000);
33572 var anchor = this.getSlideAnchor();
33573 this.el.slideOut(anchor, {
33574 callback : function(){
33575 this.el.setStyle("z-index", "");
33576 this.collapsedEl.slideIn(anchor, {duration:.3});
33578 this.el.setLocation(-10000,-10000);
33580 this.fireEvent("collapsed", this);
33587 animateExpand : function(){
33588 this.beforeSlide();
33589 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33590 this.el.setStyle("z-index", 20000);
33591 this.collapsedEl.hide({
33594 this.el.slideIn(this.getSlideAnchor(), {
33595 callback : function(){
33596 this.el.setStyle("z-index", "");
33599 this.split.el.show();
33601 this.fireEvent("invalidated", this);
33602 this.fireEvent("expanded", this);
33630 getAnchor : function(){
33631 return this.anchors[this.position];
33634 getCollapseAnchor : function(){
33635 return this.canchors[this.position];
33638 getSlideAnchor : function(){
33639 return this.sanchors[this.position];
33642 getAlignAdj : function(){
33643 var cm = this.cmargins;
33644 switch(this.position){
33660 getExpandAdj : function(){
33661 var c = this.collapsedEl, cm = this.cmargins;
33662 switch(this.position){
33664 return [-(cm.right+c.getWidth()+cm.left), 0];
33667 return [cm.right+c.getWidth()+cm.left, 0];
33670 return [0, -(cm.top+cm.bottom+c.getHeight())];
33673 return [0, cm.top+cm.bottom+c.getHeight()];
33679 * Ext JS Library 1.1.1
33680 * Copyright(c) 2006-2007, Ext JS, LLC.
33682 * Originally Released Under LGPL - original licence link has changed is not relivant.
33685 * <script type="text/javascript">
33688 * These classes are private internal classes
33690 Roo.bootstrap.layout.Center = function(config){
33691 config.region = "center";
33692 Roo.bootstrap.layout.Region.call(this, config);
33693 this.visible = true;
33694 this.minWidth = config.minWidth || 20;
33695 this.minHeight = config.minHeight || 20;
33698 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33700 // center panel can't be hidden
33704 // center panel can't be hidden
33707 getMinWidth: function(){
33708 return this.minWidth;
33711 getMinHeight: function(){
33712 return this.minHeight;
33725 Roo.bootstrap.layout.North = function(config)
33727 config.region = 'north';
33728 config.cursor = 'n-resize';
33730 Roo.bootstrap.layout.Split.call(this, config);
33734 this.split.placement = Roo.bootstrap.SplitBar.TOP;
33735 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33736 this.split.el.addClass("roo-layout-split-v");
33738 var size = config.initialSize || config.height;
33739 if(typeof size != "undefined"){
33740 this.el.setHeight(size);
33743 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33745 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33749 getBox : function(){
33750 if(this.collapsed){
33751 return this.collapsedEl.getBox();
33753 var box = this.el.getBox();
33755 box.height += this.split.el.getHeight();
33760 updateBox : function(box){
33761 if(this.split && !this.collapsed){
33762 box.height -= this.split.el.getHeight();
33763 this.split.el.setLeft(box.x);
33764 this.split.el.setTop(box.y+box.height);
33765 this.split.el.setWidth(box.width);
33767 if(this.collapsed){
33768 this.updateBody(box.width, null);
33770 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33778 Roo.bootstrap.layout.South = function(config){
33779 config.region = 'south';
33780 config.cursor = 's-resize';
33781 Roo.bootstrap.layout.Split.call(this, config);
33783 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33784 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33785 this.split.el.addClass("roo-layout-split-v");
33787 var size = config.initialSize || config.height;
33788 if(typeof size != "undefined"){
33789 this.el.setHeight(size);
33793 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33794 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33795 getBox : function(){
33796 if(this.collapsed){
33797 return this.collapsedEl.getBox();
33799 var box = this.el.getBox();
33801 var sh = this.split.el.getHeight();
33808 updateBox : function(box){
33809 if(this.split && !this.collapsed){
33810 var sh = this.split.el.getHeight();
33813 this.split.el.setLeft(box.x);
33814 this.split.el.setTop(box.y-sh);
33815 this.split.el.setWidth(box.width);
33817 if(this.collapsed){
33818 this.updateBody(box.width, null);
33820 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33824 Roo.bootstrap.layout.East = function(config){
33825 config.region = "east";
33826 config.cursor = "e-resize";
33827 Roo.bootstrap.layout.Split.call(this, config);
33829 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33830 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33831 this.split.el.addClass("roo-layout-split-h");
33833 var size = config.initialSize || config.width;
33834 if(typeof size != "undefined"){
33835 this.el.setWidth(size);
33838 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33839 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33840 getBox : function(){
33841 if(this.collapsed){
33842 return this.collapsedEl.getBox();
33844 var box = this.el.getBox();
33846 var sw = this.split.el.getWidth();
33853 updateBox : function(box){
33854 if(this.split && !this.collapsed){
33855 var sw = this.split.el.getWidth();
33857 this.split.el.setLeft(box.x);
33858 this.split.el.setTop(box.y);
33859 this.split.el.setHeight(box.height);
33862 if(this.collapsed){
33863 this.updateBody(null, box.height);
33865 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33869 Roo.bootstrap.layout.West = function(config){
33870 config.region = "west";
33871 config.cursor = "w-resize";
33873 Roo.bootstrap.layout.Split.call(this, config);
33875 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33876 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33877 this.split.el.addClass("roo-layout-split-h");
33881 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33882 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33884 onRender: function(ctr, pos)
33886 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33887 var size = this.config.initialSize || this.config.width;
33888 if(typeof size != "undefined"){
33889 this.el.setWidth(size);
33893 getBox : function(){
33894 if(this.collapsed){
33895 return this.collapsedEl.getBox();
33897 var box = this.el.getBox();
33899 box.width += this.split.el.getWidth();
33904 updateBox : function(box){
33905 if(this.split && !this.collapsed){
33906 var sw = this.split.el.getWidth();
33908 this.split.el.setLeft(box.x+box.width);
33909 this.split.el.setTop(box.y);
33910 this.split.el.setHeight(box.height);
33912 if(this.collapsed){
33913 this.updateBody(null, box.height);
33915 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33918 Roo.namespace("Roo.bootstrap.panel");/*
33920 * Ext JS Library 1.1.1
33921 * Copyright(c) 2006-2007, Ext JS, LLC.
33923 * Originally Released Under LGPL - original licence link has changed is not relivant.
33926 * <script type="text/javascript">
33929 * @class Roo.ContentPanel
33930 * @extends Roo.util.Observable
33931 * A basic ContentPanel element.
33932 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
33933 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
33934 * @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
33935 * @cfg {Boolean} closable True if the panel can be closed/removed
33936 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
33937 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33938 * @cfg {Toolbar} toolbar A toolbar for this panel
33939 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
33940 * @cfg {String} title The title for this panel
33941 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33942 * @cfg {String} url Calls {@link #setUrl} with this value
33943 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33944 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
33945 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
33946 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
33949 * Create a new ContentPanel.
33950 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33951 * @param {String/Object} config A string to set only the title or a config object
33952 * @param {String} content (optional) Set the HTML content for this panel
33953 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33955 Roo.bootstrap.panel.Content = function( config){
33957 var el = config.el;
33958 var content = config.content;
33960 if(config.autoCreate){ // xtype is available if this is called from factory
33963 this.el = Roo.get(el);
33964 if(!this.el && config && config.autoCreate){
33965 if(typeof config.autoCreate == "object"){
33966 if(!config.autoCreate.id){
33967 config.autoCreate.id = config.id||el;
33969 this.el = Roo.DomHelper.append(document.body,
33970 config.autoCreate, true);
33972 var elcfg = { tag: "div",
33973 cls: "roo-layout-inactive-content",
33977 elcfg.html = config.html;
33981 this.el = Roo.DomHelper.append(document.body, elcfg , true);
33984 this.closable = false;
33985 this.loaded = false;
33986 this.active = false;
33989 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33991 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33993 this.wrapEl = this.el.wrap();
33995 if (config.toolbar.items) {
33996 ti = config.toolbar.items ;
33997 delete config.toolbar.items ;
34001 this.toolbar.render(this.wrapEl, 'before');
34002 for(var i =0;i < ti.length;i++) {
34003 // Roo.log(['add child', items[i]]);
34004 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34006 this.toolbar.items = nitems;
34007 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34008 delete config.toolbar;
34012 // xtype created footer. - not sure if will work as we normally have to render first..
34013 if (this.footer && !this.footer.el && this.footer.xtype) {
34014 if (!this.wrapEl) {
34015 this.wrapEl = this.el.wrap();
34018 this.footer.container = this.wrapEl.createChild();
34020 this.footer = Roo.factory(this.footer, Roo);
34025 if(typeof config == "string"){
34026 this.title = config;
34028 Roo.apply(this, config);
34032 this.resizeEl = Roo.get(this.resizeEl, true);
34034 this.resizeEl = this.el;
34036 // handle view.xtype
34044 * Fires when this panel is activated.
34045 * @param {Roo.ContentPanel} this
34049 * @event deactivate
34050 * Fires when this panel is activated.
34051 * @param {Roo.ContentPanel} this
34053 "deactivate" : true,
34057 * Fires when this panel is resized if fitToFrame is true.
34058 * @param {Roo.ContentPanel} this
34059 * @param {Number} width The width after any component adjustments
34060 * @param {Number} height The height after any component adjustments
34066 * Fires when this tab is created
34067 * @param {Roo.ContentPanel} this
34078 if(this.autoScroll){
34079 this.resizeEl.setStyle("overflow", "auto");
34081 // fix randome scrolling
34082 //this.el.on('scroll', function() {
34083 // Roo.log('fix random scolling');
34084 // this.scrollTo('top',0);
34087 content = content || this.content;
34089 this.setContent(content);
34091 if(config && config.url){
34092 this.setUrl(this.url, this.params, this.loadOnce);
34097 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34099 if (this.view && typeof(this.view.xtype) != 'undefined') {
34100 this.view.el = this.el.appendChild(document.createElement("div"));
34101 this.view = Roo.factory(this.view);
34102 this.view.render && this.view.render(false, '');
34106 this.fireEvent('render', this);
34109 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34111 setRegion : function(region){
34112 this.region = region;
34114 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
34116 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
34121 * Returns the toolbar for this Panel if one was configured.
34122 * @return {Roo.Toolbar}
34124 getToolbar : function(){
34125 return this.toolbar;
34128 setActiveState : function(active){
34129 this.active = active;
34131 this.fireEvent("deactivate", this);
34133 this.fireEvent("activate", this);
34137 * Updates this panel's element
34138 * @param {String} content The new content
34139 * @param {Boolean} loadScripts (optional) true to look for and process scripts
34141 setContent : function(content, loadScripts){
34142 this.el.update(content, loadScripts);
34145 ignoreResize : function(w, h){
34146 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34149 this.lastSize = {width: w, height: h};
34154 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34155 * @return {Roo.UpdateManager} The UpdateManager
34157 getUpdateManager : function(){
34158 return this.el.getUpdateManager();
34161 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34162 * @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:
34165 url: "your-url.php",
34166 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34167 callback: yourFunction,
34168 scope: yourObject, //(optional scope)
34171 text: "Loading...",
34176 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34177 * 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.
34178 * @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}
34179 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34180 * @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.
34181 * @return {Roo.ContentPanel} this
34184 var um = this.el.getUpdateManager();
34185 um.update.apply(um, arguments);
34191 * 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.
34192 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34193 * @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)
34194 * @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)
34195 * @return {Roo.UpdateManager} The UpdateManager
34197 setUrl : function(url, params, loadOnce){
34198 if(this.refreshDelegate){
34199 this.removeListener("activate", this.refreshDelegate);
34201 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34202 this.on("activate", this.refreshDelegate);
34203 return this.el.getUpdateManager();
34206 _handleRefresh : function(url, params, loadOnce){
34207 if(!loadOnce || !this.loaded){
34208 var updater = this.el.getUpdateManager();
34209 updater.update(url, params, this._setLoaded.createDelegate(this));
34213 _setLoaded : function(){
34214 this.loaded = true;
34218 * Returns this panel's id
34221 getId : function(){
34226 * Returns this panel's element - used by regiosn to add.
34227 * @return {Roo.Element}
34229 getEl : function(){
34230 return this.wrapEl || this.el;
34235 adjustForComponents : function(width, height)
34237 //Roo.log('adjustForComponents ');
34238 if(this.resizeEl != this.el){
34239 width -= this.el.getFrameWidth('lr');
34240 height -= this.el.getFrameWidth('tb');
34243 var te = this.toolbar.getEl();
34244 height -= te.getHeight();
34245 te.setWidth(width);
34248 var te = this.footer.getEl();
34249 Roo.log("footer:" + te.getHeight());
34251 height -= te.getHeight();
34252 te.setWidth(width);
34256 if(this.adjustments){
34257 width += this.adjustments[0];
34258 height += this.adjustments[1];
34260 return {"width": width, "height": height};
34263 setSize : function(width, height){
34264 if(this.fitToFrame && !this.ignoreResize(width, height)){
34265 if(this.fitContainer && this.resizeEl != this.el){
34266 this.el.setSize(width, height);
34268 var size = this.adjustForComponents(width, height);
34269 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34270 this.fireEvent('resize', this, size.width, size.height);
34275 * Returns this panel's title
34278 getTitle : function(){
34283 * Set this panel's title
34284 * @param {String} title
34286 setTitle : function(title){
34287 this.title = title;
34289 this.region.updatePanelTitle(this, title);
34294 * Returns true is this panel was configured to be closable
34295 * @return {Boolean}
34297 isClosable : function(){
34298 return this.closable;
34301 beforeSlide : function(){
34303 this.resizeEl.clip();
34306 afterSlide : function(){
34308 this.resizeEl.unclip();
34312 * Force a content refresh from the URL specified in the {@link #setUrl} method.
34313 * Will fail silently if the {@link #setUrl} method has not been called.
34314 * This does not activate the panel, just updates its content.
34316 refresh : function(){
34317 if(this.refreshDelegate){
34318 this.loaded = false;
34319 this.refreshDelegate();
34324 * Destroys this panel
34326 destroy : function(){
34327 this.el.removeAllListeners();
34328 var tempEl = document.createElement("span");
34329 tempEl.appendChild(this.el.dom);
34330 tempEl.innerHTML = "";
34336 * form - if the content panel contains a form - this is a reference to it.
34337 * @type {Roo.form.Form}
34341 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34342 * This contains a reference to it.
34348 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34358 * @param {Object} cfg Xtype definition of item to add.
34362 getChildContainer: function () {
34363 return this.getEl();
34368 var ret = new Roo.factory(cfg);
34373 if (cfg.xtype.match(/^Form$/)) {
34376 //if (this.footer) {
34377 // el = this.footer.container.insertSibling(false, 'before');
34379 el = this.el.createChild();
34382 this.form = new Roo.form.Form(cfg);
34385 if ( this.form.allItems.length) {
34386 this.form.render(el.dom);
34390 // should only have one of theses..
34391 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34392 // views.. should not be just added - used named prop 'view''
34394 cfg.el = this.el.appendChild(document.createElement("div"));
34397 var ret = new Roo.factory(cfg);
34399 ret.render && ret.render(false, ''); // render blank..
34409 * @class Roo.bootstrap.panel.Grid
34410 * @extends Roo.bootstrap.panel.Content
34412 * Create a new GridPanel.
34413 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34414 * @param {Object} config A the config object
34420 Roo.bootstrap.panel.Grid = function(config)
34424 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34425 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34427 config.el = this.wrapper;
34428 //this.el = this.wrapper;
34430 if (config.container) {
34431 // ctor'ed from a Border/panel.grid
34434 this.wrapper.setStyle("overflow", "hidden");
34435 this.wrapper.addClass('roo-grid-container');
34440 if(config.toolbar){
34441 var tool_el = this.wrapper.createChild();
34442 this.toolbar = Roo.factory(config.toolbar);
34444 if (config.toolbar.items) {
34445 ti = config.toolbar.items ;
34446 delete config.toolbar.items ;
34450 this.toolbar.render(tool_el);
34451 for(var i =0;i < ti.length;i++) {
34452 // Roo.log(['add child', items[i]]);
34453 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34455 this.toolbar.items = nitems;
34457 delete config.toolbar;
34460 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34461 config.grid.scrollBody = true;;
34462 config.grid.monitorWindowResize = false; // turn off autosizing
34463 config.grid.autoHeight = false;
34464 config.grid.autoWidth = false;
34466 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34468 if (config.background) {
34469 // render grid on panel activation (if panel background)
34470 this.on('activate', function(gp) {
34471 if (!gp.grid.rendered) {
34472 gp.grid.render(el);
34473 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34479 this.grid.render(this.wrapper);
34480 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34483 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34484 // ??? needed ??? config.el = this.wrapper;
34489 // xtype created footer. - not sure if will work as we normally have to render first..
34490 if (this.footer && !this.footer.el && this.footer.xtype) {
34492 var ctr = this.grid.getView().getFooterPanel(true);
34493 this.footer.dataSource = this.grid.dataSource;
34494 this.footer = Roo.factory(this.footer, Roo);
34495 this.footer.render(ctr);
34505 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34506 getId : function(){
34507 return this.grid.id;
34511 * Returns the grid for this panel
34512 * @return {Roo.bootstrap.Table}
34514 getGrid : function(){
34518 setSize : function(width, height){
34519 if(!this.ignoreResize(width, height)){
34520 var grid = this.grid;
34521 var size = this.adjustForComponents(width, height);
34522 var gridel = grid.getGridEl();
34523 gridel.setSize(size.width, size.height);
34525 var thd = grid.getGridEl().select('thead',true).first();
34526 var tbd = grid.getGridEl().select('tbody', true).first();
34528 tbd.setSize(width, height - thd.getHeight());
34537 beforeSlide : function(){
34538 this.grid.getView().scroller.clip();
34541 afterSlide : function(){
34542 this.grid.getView().scroller.unclip();
34545 destroy : function(){
34546 this.grid.destroy();
34548 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
34553 * @class Roo.bootstrap.panel.Nest
34554 * @extends Roo.bootstrap.panel.Content
34556 * Create a new Panel, that can contain a layout.Border.
34559 * @param {Roo.BorderLayout} layout The layout for this panel
34560 * @param {String/Object} config A string to set only the title or a config object
34562 Roo.bootstrap.panel.Nest = function(config)
34564 // construct with only one argument..
34565 /* FIXME - implement nicer consturctors
34566 if (layout.layout) {
34568 layout = config.layout;
34569 delete config.layout;
34571 if (layout.xtype && !layout.getEl) {
34572 // then layout needs constructing..
34573 layout = Roo.factory(layout, Roo);
34577 config.el = config.layout.getEl();
34579 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34581 config.layout.monitorWindowResize = false; // turn off autosizing
34582 this.layout = config.layout;
34583 this.layout.getEl().addClass("roo-layout-nested-layout");
34590 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34592 setSize : function(width, height){
34593 if(!this.ignoreResize(width, height)){
34594 var size = this.adjustForComponents(width, height);
34595 var el = this.layout.getEl();
34596 if (size.height < 1) {
34597 el.setWidth(size.width);
34599 el.setSize(size.width, size.height);
34601 var touch = el.dom.offsetWidth;
34602 this.layout.layout();
34603 // ie requires a double layout on the first pass
34604 if(Roo.isIE && !this.initialized){
34605 this.initialized = true;
34606 this.layout.layout();
34611 // activate all subpanels if not currently active..
34613 setActiveState : function(active){
34614 this.active = active;
34616 this.fireEvent("deactivate", this);
34620 this.fireEvent("activate", this);
34621 // not sure if this should happen before or after..
34622 if (!this.layout) {
34623 return; // should not happen..
34626 for (var r in this.layout.regions) {
34627 reg = this.layout.getRegion(r);
34628 if (reg.getActivePanel()) {
34629 //reg.showPanel(reg.getActivePanel()); // force it to activate..
34630 reg.setActivePanel(reg.getActivePanel());
34633 if (!reg.panels.length) {
34636 reg.showPanel(reg.getPanel(0));
34645 * Returns the nested BorderLayout for this panel
34646 * @return {Roo.BorderLayout}
34648 getLayout : function(){
34649 return this.layout;
34653 * Adds a xtype elements to the layout of the nested panel
34657 xtype : 'ContentPanel',
34664 xtype : 'NestedLayoutPanel',
34670 items : [ ... list of content panels or nested layout panels.. ]
34674 * @param {Object} cfg Xtype definition of item to add.
34676 addxtype : function(cfg) {
34677 return this.layout.addxtype(cfg);
34682 * Ext JS Library 1.1.1
34683 * Copyright(c) 2006-2007, Ext JS, LLC.
34685 * Originally Released Under LGPL - original licence link has changed is not relivant.
34688 * <script type="text/javascript">
34691 * @class Roo.TabPanel
34692 * @extends Roo.util.Observable
34693 * A lightweight tab container.
34697 // basic tabs 1, built from existing content
34698 var tabs = new Roo.TabPanel("tabs1");
34699 tabs.addTab("script", "View Script");
34700 tabs.addTab("markup", "View Markup");
34701 tabs.activate("script");
34703 // more advanced tabs, built from javascript
34704 var jtabs = new Roo.TabPanel("jtabs");
34705 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34707 // set up the UpdateManager
34708 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34709 var updater = tab2.getUpdateManager();
34710 updater.setDefaultUrl("ajax1.htm");
34711 tab2.on('activate', updater.refresh, updater, true);
34713 // Use setUrl for Ajax loading
34714 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34715 tab3.setUrl("ajax2.htm", null, true);
34718 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34721 jtabs.activate("jtabs-1");
34724 * Create a new TabPanel.
34725 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34726 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34728 Roo.bootstrap.panel.Tabs = function(config){
34730 * The container element for this TabPanel.
34731 * @type Roo.Element
34733 this.el = Roo.get(config.el);
34736 if(typeof config == "boolean"){
34737 this.tabPosition = config ? "bottom" : "top";
34739 Roo.apply(this, config);
34743 if(this.tabPosition == "bottom"){
34744 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34745 this.el.addClass("roo-tabs-bottom");
34747 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34748 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34749 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34751 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34753 if(this.tabPosition != "bottom"){
34754 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34755 * @type Roo.Element
34757 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34758 this.el.addClass("roo-tabs-top");
34762 this.bodyEl.setStyle("position", "relative");
34764 this.active = null;
34765 this.activateDelegate = this.activate.createDelegate(this);
34770 * Fires when the active tab changes
34771 * @param {Roo.TabPanel} this
34772 * @param {Roo.TabPanelItem} activePanel The new active tab
34776 * @event beforetabchange
34777 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34778 * @param {Roo.TabPanel} this
34779 * @param {Object} e Set cancel to true on this object to cancel the tab change
34780 * @param {Roo.TabPanelItem} tab The tab being changed to
34782 "beforetabchange" : true
34785 Roo.EventManager.onWindowResize(this.onResize, this);
34786 this.cpad = this.el.getPadding("lr");
34787 this.hiddenCount = 0;
34790 // toolbar on the tabbar support...
34791 if (this.toolbar) {
34792 alert("no toolbar support yet");
34793 this.toolbar = false;
34795 var tcfg = this.toolbar;
34796 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
34797 this.toolbar = new Roo.Toolbar(tcfg);
34798 if (Roo.isSafari) {
34799 var tbl = tcfg.container.child('table', true);
34800 tbl.setAttribute('width', '100%');
34808 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34811 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34813 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34815 tabPosition : "top",
34817 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34819 currentTabWidth : 0,
34821 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34825 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34829 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34831 preferredTabWidth : 175,
34833 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34835 resizeTabs : false,
34837 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34839 monitorResize : true,
34841 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
34846 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34847 * @param {String} id The id of the div to use <b>or create</b>
34848 * @param {String} text The text for the tab
34849 * @param {String} content (optional) Content to put in the TabPanelItem body
34850 * @param {Boolean} closable (optional) True to create a close icon on the tab
34851 * @return {Roo.TabPanelItem} The created TabPanelItem
34853 addTab : function(id, text, content, closable)
34855 var item = new Roo.bootstrap.panel.TabItem({
34859 closable : closable
34861 this.addTabItem(item);
34863 item.setContent(content);
34869 * Returns the {@link Roo.TabPanelItem} with the specified id/index
34870 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34871 * @return {Roo.TabPanelItem}
34873 getTab : function(id){
34874 return this.items[id];
34878 * Hides the {@link Roo.TabPanelItem} with the specified id/index
34879 * @param {String/Number} id The id or index of the TabPanelItem to hide.
34881 hideTab : function(id){
34882 var t = this.items[id];
34885 this.hiddenCount++;
34886 this.autoSizeTabs();
34891 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34892 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34894 unhideTab : function(id){
34895 var t = this.items[id];
34897 t.setHidden(false);
34898 this.hiddenCount--;
34899 this.autoSizeTabs();
34904 * Adds an existing {@link Roo.TabPanelItem}.
34905 * @param {Roo.TabPanelItem} item The TabPanelItem to add
34907 addTabItem : function(item){
34908 this.items[item.id] = item;
34909 this.items.push(item);
34910 // if(this.resizeTabs){
34911 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34912 // this.autoSizeTabs();
34914 // item.autoSize();
34919 * Removes a {@link Roo.TabPanelItem}.
34920 * @param {String/Number} id The id or index of the TabPanelItem to remove.
34922 removeTab : function(id){
34923 var items = this.items;
34924 var tab = items[id];
34925 if(!tab) { return; }
34926 var index = items.indexOf(tab);
34927 if(this.active == tab && items.length > 1){
34928 var newTab = this.getNextAvailable(index);
34933 this.stripEl.dom.removeChild(tab.pnode.dom);
34934 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34935 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34937 items.splice(index, 1);
34938 delete this.items[tab.id];
34939 tab.fireEvent("close", tab);
34940 tab.purgeListeners();
34941 this.autoSizeTabs();
34944 getNextAvailable : function(start){
34945 var items = this.items;
34947 // look for a next tab that will slide over to
34948 // replace the one being removed
34949 while(index < items.length){
34950 var item = items[++index];
34951 if(item && !item.isHidden()){
34955 // if one isn't found select the previous tab (on the left)
34958 var item = items[--index];
34959 if(item && !item.isHidden()){
34967 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34968 * @param {String/Number} id The id or index of the TabPanelItem to disable.
34970 disableTab : function(id){
34971 var tab = this.items[id];
34972 if(tab && this.active != tab){
34978 * Enables a {@link Roo.TabPanelItem} that is disabled.
34979 * @param {String/Number} id The id or index of the TabPanelItem to enable.
34981 enableTab : function(id){
34982 var tab = this.items[id];
34987 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34988 * @param {String/Number} id The id or index of the TabPanelItem to activate.
34989 * @return {Roo.TabPanelItem} The TabPanelItem.
34991 activate : function(id){
34992 var tab = this.items[id];
34996 if(tab == this.active || tab.disabled){
35000 this.fireEvent("beforetabchange", this, e, tab);
35001 if(e.cancel !== true && !tab.disabled){
35003 this.active.hide();
35005 this.active = this.items[id];
35006 this.active.show();
35007 this.fireEvent("tabchange", this, this.active);
35013 * Gets the active {@link Roo.TabPanelItem}.
35014 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35016 getActiveTab : function(){
35017 return this.active;
35021 * Updates the tab body element to fit the height of the container element
35022 * for overflow scrolling
35023 * @param {Number} targetHeight (optional) Override the starting height from the elements height
35025 syncHeight : function(targetHeight){
35026 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35027 var bm = this.bodyEl.getMargins();
35028 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35029 this.bodyEl.setHeight(newHeight);
35033 onResize : function(){
35034 if(this.monitorResize){
35035 this.autoSizeTabs();
35040 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35042 beginUpdate : function(){
35043 this.updating = true;
35047 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35049 endUpdate : function(){
35050 this.updating = false;
35051 this.autoSizeTabs();
35055 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35057 autoSizeTabs : function(){
35058 var count = this.items.length;
35059 var vcount = count - this.hiddenCount;
35060 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35063 var w = Math.max(this.el.getWidth() - this.cpad, 10);
35064 var availWidth = Math.floor(w / vcount);
35065 var b = this.stripBody;
35066 if(b.getWidth() > w){
35067 var tabs = this.items;
35068 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35069 if(availWidth < this.minTabWidth){
35070 /*if(!this.sleft){ // incomplete scrolling code
35071 this.createScrollButtons();
35074 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35077 if(this.currentTabWidth < this.preferredTabWidth){
35078 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35084 * Returns the number of tabs in this TabPanel.
35087 getCount : function(){
35088 return this.items.length;
35092 * Resizes all the tabs to the passed width
35093 * @param {Number} The new width
35095 setTabWidth : function(width){
35096 this.currentTabWidth = width;
35097 for(var i = 0, len = this.items.length; i < len; i++) {
35098 if(!this.items[i].isHidden()) {
35099 this.items[i].setWidth(width);
35105 * Destroys this TabPanel
35106 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
35108 destroy : function(removeEl){
35109 Roo.EventManager.removeResizeListener(this.onResize, this);
35110 for(var i = 0, len = this.items.length; i < len; i++){
35111 this.items[i].purgeListeners();
35113 if(removeEl === true){
35114 this.el.update("");
35119 createStrip : function(container)
35121 var strip = document.createElement("nav");
35122 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
35123 container.appendChild(strip);
35127 createStripList : function(strip)
35129 // div wrapper for retard IE
35130 // returns the "tr" element.
35131 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
35132 //'<div class="x-tabs-strip-wrap">'+
35133 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
35134 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
35135 return strip.firstChild; //.firstChild.firstChild.firstChild;
35137 createBody : function(container)
35139 var body = document.createElement("div");
35140 Roo.id(body, "tab-body");
35141 //Roo.fly(body).addClass("x-tabs-body");
35142 Roo.fly(body).addClass("tab-content");
35143 container.appendChild(body);
35146 createItemBody :function(bodyEl, id){
35147 var body = Roo.getDom(id);
35149 body = document.createElement("div");
35152 //Roo.fly(body).addClass("x-tabs-item-body");
35153 Roo.fly(body).addClass("tab-pane");
35154 bodyEl.insertBefore(body, bodyEl.firstChild);
35158 createStripElements : function(stripEl, text, closable)
35160 var td = document.createElement("li"); // was td..
35163 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
35166 stripEl.appendChild(td);
35168 td.className = "x-tabs-closable";
35169 if(!this.closeTpl){
35170 this.closeTpl = new Roo.Template(
35171 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35172 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
35173 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
35176 var el = this.closeTpl.overwrite(td, {"text": text});
35177 var close = el.getElementsByTagName("div")[0];
35178 var inner = el.getElementsByTagName("em")[0];
35179 return {"el": el, "close": close, "inner": inner};
35182 // not sure what this is..
35184 //this.tabTpl = new Roo.Template(
35185 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35186 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
35188 this.tabTpl = new Roo.Template(
35190 '<span unselectable="on"' +
35191 (this.disableTooltips ? '' : ' title="{text}"') +
35192 ' >{text}</span></span></a>'
35196 var el = this.tabTpl.overwrite(td, {"text": text});
35197 var inner = el.getElementsByTagName("span")[0];
35198 return {"el": el, "inner": inner};
35206 * @class Roo.TabPanelItem
35207 * @extends Roo.util.Observable
35208 * Represents an individual item (tab plus body) in a TabPanel.
35209 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35210 * @param {String} id The id of this TabPanelItem
35211 * @param {String} text The text for the tab of this TabPanelItem
35212 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35214 Roo.bootstrap.panel.TabItem = function(config){
35216 * The {@link Roo.TabPanel} this TabPanelItem belongs to
35217 * @type Roo.TabPanel
35219 this.tabPanel = config.panel;
35221 * The id for this TabPanelItem
35224 this.id = config.id;
35226 this.disabled = false;
35228 this.text = config.text;
35230 this.loaded = false;
35231 this.closable = config.closable;
35234 * The body element for this TabPanelItem.
35235 * @type Roo.Element
35237 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35238 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35239 this.bodyEl.setStyle("display", "block");
35240 this.bodyEl.setStyle("zoom", "1");
35241 //this.hideAction();
35243 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
35245 this.el = Roo.get(els.el);
35246 this.inner = Roo.get(els.inner, true);
35247 this.textEl = Roo.get(this.el.dom.firstChild, true);
35248 this.pnode = Roo.get(els.el.parentNode, true);
35249 this.el.on("mousedown", this.onTabMouseDown, this);
35250 this.el.on("click", this.onTabClick, this);
35252 if(config.closable){
35253 var c = Roo.get(els.close, true);
35254 c.dom.title = this.closeText;
35255 c.addClassOnOver("close-over");
35256 c.on("click", this.closeClick, this);
35262 * Fires when this tab becomes the active tab.
35263 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35264 * @param {Roo.TabPanelItem} this
35268 * @event beforeclose
35269 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35270 * @param {Roo.TabPanelItem} this
35271 * @param {Object} e Set cancel to true on this object to cancel the close.
35273 "beforeclose": true,
35276 * Fires when this tab is closed.
35277 * @param {Roo.TabPanelItem} this
35281 * @event deactivate
35282 * Fires when this tab is no longer the active tab.
35283 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35284 * @param {Roo.TabPanelItem} this
35286 "deactivate" : true
35288 this.hidden = false;
35290 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35293 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35295 purgeListeners : function(){
35296 Roo.util.Observable.prototype.purgeListeners.call(this);
35297 this.el.removeAllListeners();
35300 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35303 this.pnode.addClass("active");
35306 this.tabPanel.stripWrap.repaint();
35308 this.fireEvent("activate", this.tabPanel, this);
35312 * Returns true if this tab is the active tab.
35313 * @return {Boolean}
35315 isActive : function(){
35316 return this.tabPanel.getActiveTab() == this;
35320 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35323 this.pnode.removeClass("active");
35325 this.fireEvent("deactivate", this.tabPanel, this);
35328 hideAction : function(){
35329 this.bodyEl.hide();
35330 this.bodyEl.setStyle("position", "absolute");
35331 this.bodyEl.setLeft("-20000px");
35332 this.bodyEl.setTop("-20000px");
35335 showAction : function(){
35336 this.bodyEl.setStyle("position", "relative");
35337 this.bodyEl.setTop("");
35338 this.bodyEl.setLeft("");
35339 this.bodyEl.show();
35343 * Set the tooltip for the tab.
35344 * @param {String} tooltip The tab's tooltip
35346 setTooltip : function(text){
35347 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35348 this.textEl.dom.qtip = text;
35349 this.textEl.dom.removeAttribute('title');
35351 this.textEl.dom.title = text;
35355 onTabClick : function(e){
35356 e.preventDefault();
35357 this.tabPanel.activate(this.id);
35360 onTabMouseDown : function(e){
35361 e.preventDefault();
35362 this.tabPanel.activate(this.id);
35365 getWidth : function(){
35366 return this.inner.getWidth();
35369 setWidth : function(width){
35370 var iwidth = width - this.pnode.getPadding("lr");
35371 this.inner.setWidth(iwidth);
35372 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35373 this.pnode.setWidth(width);
35377 * Show or hide the tab
35378 * @param {Boolean} hidden True to hide or false to show.
35380 setHidden : function(hidden){
35381 this.hidden = hidden;
35382 this.pnode.setStyle("display", hidden ? "none" : "");
35386 * Returns true if this tab is "hidden"
35387 * @return {Boolean}
35389 isHidden : function(){
35390 return this.hidden;
35394 * Returns the text for this tab
35397 getText : function(){
35401 autoSize : function(){
35402 //this.el.beginMeasure();
35403 this.textEl.setWidth(1);
35405 * #2804 [new] Tabs in Roojs
35406 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35408 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35409 //this.el.endMeasure();
35413 * Sets the text for the tab (Note: this also sets the tooltip text)
35414 * @param {String} text The tab's text and tooltip
35416 setText : function(text){
35418 this.textEl.update(text);
35419 this.setTooltip(text);
35420 //if(!this.tabPanel.resizeTabs){
35421 // this.autoSize();
35425 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35427 activate : function(){
35428 this.tabPanel.activate(this.id);
35432 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35434 disable : function(){
35435 if(this.tabPanel.active != this){
35436 this.disabled = true;
35437 this.pnode.addClass("disabled");
35442 * Enables this TabPanelItem if it was previously disabled.
35444 enable : function(){
35445 this.disabled = false;
35446 this.pnode.removeClass("disabled");
35450 * Sets the content for this TabPanelItem.
35451 * @param {String} content The content
35452 * @param {Boolean} loadScripts true to look for and load scripts
35454 setContent : function(content, loadScripts){
35455 this.bodyEl.update(content, loadScripts);
35459 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35460 * @return {Roo.UpdateManager} The UpdateManager
35462 getUpdateManager : function(){
35463 return this.bodyEl.getUpdateManager();
35467 * Set a URL to be used to load the content for this TabPanelItem.
35468 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35469 * @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)
35470 * @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)
35471 * @return {Roo.UpdateManager} The UpdateManager
35473 setUrl : function(url, params, loadOnce){
35474 if(this.refreshDelegate){
35475 this.un('activate', this.refreshDelegate);
35477 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35478 this.on("activate", this.refreshDelegate);
35479 return this.bodyEl.getUpdateManager();
35483 _handleRefresh : function(url, params, loadOnce){
35484 if(!loadOnce || !this.loaded){
35485 var updater = this.bodyEl.getUpdateManager();
35486 updater.update(url, params, this._setLoaded.createDelegate(this));
35491 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
35492 * Will fail silently if the setUrl method has not been called.
35493 * This does not activate the panel, just updates its content.
35495 refresh : function(){
35496 if(this.refreshDelegate){
35497 this.loaded = false;
35498 this.refreshDelegate();
35503 _setLoaded : function(){
35504 this.loaded = true;
35508 closeClick : function(e){
35511 this.fireEvent("beforeclose", this, o);
35512 if(o.cancel !== true){
35513 this.tabPanel.removeTab(this.id);
35517 * The text displayed in the tooltip for the close icon.
35520 closeText : "Close this tab"