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)
1524 this.el.dom.src = url;
1540 * @class Roo.bootstrap.Link
1541 * @extends Roo.bootstrap.Component
1542 * Bootstrap Link Class
1543 * @cfg {String} alt image alternative text
1544 * @cfg {String} href a tag href
1545 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1546 * @cfg {String} html the content of the link.
1547 * @cfg {String} anchor name for the anchor link
1548 * @cfg {String} fa - favicon
1550 * @cfg {Boolean} preventDefault (true | false) default false
1554 * Create a new Input
1555 * @param {Object} config The config object
1558 Roo.bootstrap.Link = function(config){
1559 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1565 * The img click event for the img.
1566 * @param {Roo.EventObject} e
1572 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1576 preventDefault: false,
1582 getAutoCreate : function()
1584 var html = this.html || '';
1586 if (this.fa !== false) {
1587 html = '<i class="fa fa-' + this.fa + '"></i>';
1592 // anchor's do not require html/href...
1593 if (this.anchor === false) {
1595 cfg.href = this.href || '#';
1597 cfg.name = this.anchor;
1598 if (this.html !== false || this.fa !== false) {
1601 if (this.href !== false) {
1602 cfg.href = this.href;
1606 if(this.alt !== false){
1611 if(this.target !== false) {
1612 cfg.target = this.target;
1618 initEvents: function() {
1620 if(!this.href || this.preventDefault){
1621 this.el.on('click', this.onClick, this);
1625 onClick : function(e)
1627 if(this.preventDefault){
1630 //Roo.log('img onclick');
1631 this.fireEvent('click', this, e);
1644 * @class Roo.bootstrap.Header
1645 * @extends Roo.bootstrap.Component
1646 * Bootstrap Header class
1647 * @cfg {String} html content of header
1648 * @cfg {Number} level (1|2|3|4|5|6) default 1
1651 * Create a new Header
1652 * @param {Object} config The config object
1656 Roo.bootstrap.Header = function(config){
1657 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1660 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1668 getAutoCreate : function(){
1673 tag: 'h' + (1 *this.level),
1674 html: this.html || ''
1686 * Ext JS Library 1.1.1
1687 * Copyright(c) 2006-2007, Ext JS, LLC.
1689 * Originally Released Under LGPL - original licence link has changed is not relivant.
1692 * <script type="text/javascript">
1696 * @class Roo.bootstrap.MenuMgr
1697 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1700 Roo.bootstrap.MenuMgr = function(){
1701 var menus, active, groups = {}, attached = false, lastShow = new Date();
1703 // private - called when first menu is created
1706 active = new Roo.util.MixedCollection();
1707 Roo.get(document).addKeyListener(27, function(){
1708 if(active.length > 0){
1716 if(active && active.length > 0){
1717 var c = active.clone();
1727 if(active.length < 1){
1728 Roo.get(document).un("mouseup", onMouseDown);
1736 var last = active.last();
1737 lastShow = new Date();
1740 Roo.get(document).on("mouseup", onMouseDown);
1745 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1746 m.parentMenu.activeChild = m;
1747 }else if(last && last.isVisible()){
1748 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1753 function onBeforeHide(m){
1755 m.activeChild.hide();
1757 if(m.autoHideTimer){
1758 clearTimeout(m.autoHideTimer);
1759 delete m.autoHideTimer;
1764 function onBeforeShow(m){
1765 var pm = m.parentMenu;
1766 if(!pm && !m.allowOtherMenus){
1768 }else if(pm && pm.activeChild && active != m){
1769 pm.activeChild.hide();
1773 // private this should really trigger on mouseup..
1774 function onMouseDown(e){
1775 Roo.log("on Mouse Up");
1777 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1778 Roo.log("MenuManager hideAll");
1787 function onBeforeCheck(mi, state){
1789 var g = groups[mi.group];
1790 for(var i = 0, l = g.length; i < l; i++){
1792 g[i].setChecked(false);
1801 * Hides all menus that are currently visible
1803 hideAll : function(){
1808 register : function(menu){
1812 menus[menu.id] = menu;
1813 menu.on("beforehide", onBeforeHide);
1814 menu.on("hide", onHide);
1815 menu.on("beforeshow", onBeforeShow);
1816 menu.on("show", onShow);
1818 if(g && menu.events["checkchange"]){
1822 groups[g].push(menu);
1823 menu.on("checkchange", onCheck);
1828 * Returns a {@link Roo.menu.Menu} object
1829 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1830 * be used to generate and return a new Menu instance.
1832 get : function(menu){
1833 if(typeof menu == "string"){ // menu id
1835 }else if(menu.events){ // menu instance
1838 /*else if(typeof menu.length == 'number'){ // array of menu items?
1839 return new Roo.bootstrap.Menu({items:menu});
1840 }else{ // otherwise, must be a config
1841 return new Roo.bootstrap.Menu(menu);
1848 unregister : function(menu){
1849 delete menus[menu.id];
1850 menu.un("beforehide", onBeforeHide);
1851 menu.un("hide", onHide);
1852 menu.un("beforeshow", onBeforeShow);
1853 menu.un("show", onShow);
1855 if(g && menu.events["checkchange"]){
1856 groups[g].remove(menu);
1857 menu.un("checkchange", onCheck);
1862 registerCheckable : function(menuItem){
1863 var g = menuItem.group;
1868 groups[g].push(menuItem);
1869 menuItem.on("beforecheckchange", onBeforeCheck);
1874 unregisterCheckable : function(menuItem){
1875 var g = menuItem.group;
1877 groups[g].remove(menuItem);
1878 menuItem.un("beforecheckchange", onBeforeCheck);
1890 * @class Roo.bootstrap.Menu
1891 * @extends Roo.bootstrap.Component
1892 * Bootstrap Menu class - container for MenuItems
1893 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1894 * @cfg {bool} hidden if the menu should be hidden when rendered.
1895 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1896 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1900 * @param {Object} config The config object
1904 Roo.bootstrap.Menu = function(config){
1905 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1906 if (this.registerMenu && this.type != 'treeview') {
1907 Roo.bootstrap.MenuMgr.register(this);
1912 * Fires before this menu is displayed
1913 * @param {Roo.menu.Menu} this
1918 * Fires before this menu is hidden
1919 * @param {Roo.menu.Menu} this
1924 * Fires after this menu is displayed
1925 * @param {Roo.menu.Menu} this
1930 * Fires after this menu is hidden
1931 * @param {Roo.menu.Menu} this
1936 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1937 * @param {Roo.menu.Menu} this
1938 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1939 * @param {Roo.EventObject} e
1944 * Fires when the mouse is hovering over this menu
1945 * @param {Roo.menu.Menu} this
1946 * @param {Roo.EventObject} e
1947 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1952 * Fires when the mouse exits this menu
1953 * @param {Roo.menu.Menu} this
1954 * @param {Roo.EventObject} e
1955 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1960 * Fires when a menu item contained in this menu is clicked
1961 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1962 * @param {Roo.EventObject} e
1966 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1969 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1973 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1976 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1978 registerMenu : true,
1980 menuItems :false, // stores the menu items..
1990 getChildContainer : function() {
1994 getAutoCreate : function(){
1996 //if (['right'].indexOf(this.align)!==-1) {
1997 // cfg.cn[1].cls += ' pull-right'
2003 cls : 'dropdown-menu' ,
2004 style : 'z-index:1000'
2008 if (this.type === 'submenu') {
2009 cfg.cls = 'submenu active';
2011 if (this.type === 'treeview') {
2012 cfg.cls = 'treeview-menu';
2017 initEvents : function() {
2019 // Roo.log("ADD event");
2020 // Roo.log(this.triggerEl.dom);
2022 this.triggerEl.on('click', this.onTriggerClick, this);
2024 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2026 this.triggerEl.addClass('dropdown-toggle');
2029 this.el.on('touchstart' , this.onTouch, this);
2031 this.el.on('click' , this.onClick, this);
2033 this.el.on("mouseover", this.onMouseOver, this);
2034 this.el.on("mouseout", this.onMouseOut, this);
2038 findTargetItem : function(e)
2040 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2044 //Roo.log(t); Roo.log(t.id);
2046 //Roo.log(this.menuitems);
2047 return this.menuitems.get(t.id);
2049 //return this.items.get(t.menuItemId);
2055 onTouch : function(e)
2057 Roo.log("menu.onTouch");
2058 //e.stopEvent(); this make the user popdown broken
2062 onClick : function(e)
2064 Roo.log("menu.onClick");
2066 var t = this.findTargetItem(e);
2067 if(!t || t.isContainer){
2072 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2073 if(t == this.activeItem && t.shouldDeactivate(e)){
2074 this.activeItem.deactivate();
2075 delete this.activeItem;
2079 this.setActiveItem(t, true);
2087 Roo.log('pass click event');
2091 this.fireEvent("click", this, t, e);
2095 (function() { _this.hide(); }).defer(500);
2098 onMouseOver : function(e){
2099 var t = this.findTargetItem(e);
2102 // if(t.canActivate && !t.disabled){
2103 // this.setActiveItem(t, true);
2107 this.fireEvent("mouseover", this, e, t);
2109 isVisible : function(){
2110 return !this.hidden;
2112 onMouseOut : function(e){
2113 var t = this.findTargetItem(e);
2116 // if(t == this.activeItem && t.shouldDeactivate(e)){
2117 // this.activeItem.deactivate();
2118 // delete this.activeItem;
2121 this.fireEvent("mouseout", this, e, t);
2126 * Displays this menu relative to another element
2127 * @param {String/HTMLElement/Roo.Element} element The element to align to
2128 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2129 * the element (defaults to this.defaultAlign)
2130 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2132 show : function(el, pos, parentMenu){
2133 this.parentMenu = parentMenu;
2137 this.fireEvent("beforeshow", this);
2138 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2141 * Displays this menu at a specific xy position
2142 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2143 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2145 showAt : function(xy, parentMenu, /* private: */_e){
2146 this.parentMenu = parentMenu;
2151 this.fireEvent("beforeshow", this);
2152 //xy = this.el.adjustForConstraints(xy);
2156 this.hideMenuItems();
2157 this.hidden = false;
2158 this.triggerEl.addClass('open');
2160 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2161 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2164 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2169 this.fireEvent("show", this);
2175 this.doFocus.defer(50, this);
2179 doFocus : function(){
2181 this.focusEl.focus();
2186 * Hides this menu and optionally all parent menus
2187 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2189 hide : function(deep)
2192 this.hideMenuItems();
2193 if(this.el && this.isVisible()){
2194 this.fireEvent("beforehide", this);
2195 if(this.activeItem){
2196 this.activeItem.deactivate();
2197 this.activeItem = null;
2199 this.triggerEl.removeClass('open');;
2201 this.fireEvent("hide", this);
2203 if(deep === true && this.parentMenu){
2204 this.parentMenu.hide(true);
2208 onTriggerClick : function(e)
2210 Roo.log('trigger click');
2212 var target = e.getTarget();
2214 Roo.log(target.nodeName.toLowerCase());
2216 if(target.nodeName.toLowerCase() === 'i'){
2222 onTriggerPress : function(e)
2224 Roo.log('trigger press');
2225 //Roo.log(e.getTarget());
2226 // Roo.log(this.triggerEl.dom);
2228 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2229 var pel = Roo.get(e.getTarget());
2230 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2231 Roo.log('is treeview or dropdown?');
2235 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2239 if (this.isVisible()) {
2244 this.show(this.triggerEl, false, false);
2247 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2254 hideMenuItems : function()
2256 Roo.log("hide Menu Items");
2260 //$(backdrop).remove()
2261 this.el.select('.open',true).each(function(aa) {
2263 aa.removeClass('open');
2264 //var parent = getParent($(this))
2265 //var relatedTarget = { relatedTarget: this }
2267 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2268 //if (e.isDefaultPrevented()) return
2269 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2272 addxtypeChild : function (tree, cntr) {
2273 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2275 this.menuitems.add(comp);
2296 * @class Roo.bootstrap.MenuItem
2297 * @extends Roo.bootstrap.Component
2298 * Bootstrap MenuItem class
2299 * @cfg {String} html the menu label
2300 * @cfg {String} href the link
2301 * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2302 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2303 * @cfg {Boolean} active used on sidebars to highlight active itesm
2304 * @cfg {String} fa favicon to show on left of menu item.
2305 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2309 * Create a new MenuItem
2310 * @param {Object} config The config object
2314 Roo.bootstrap.MenuItem = function(config){
2315 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2320 * The raw click event for the entire grid.
2321 * @param {Roo.bootstrap.MenuItem} this
2322 * @param {Roo.EventObject} e
2328 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2332 preventDefault: true,
2333 isContainer : false,
2337 getAutoCreate : function(){
2339 if(this.isContainer){
2342 cls: 'dropdown-menu-item'
2356 if (this.fa !== false) {
2359 cls : 'fa fa-' + this.fa
2368 cls: 'dropdown-menu-item',
2371 if (this.parent().type == 'treeview') {
2372 cfg.cls = 'treeview-menu';
2375 cfg.cls += ' active';
2380 anc.href = this.href || cfg.cn[0].href ;
2381 ctag.html = this.html || cfg.cn[0].html ;
2385 initEvents: function()
2387 if (this.parent().type == 'treeview') {
2388 this.el.select('a').on('click', this.onClick, this);
2391 this.menu.parentType = this.xtype;
2392 this.menu.triggerEl = this.el;
2393 this.menu = this.addxtype(Roo.apply({}, this.menu));
2397 onClick : function(e)
2399 Roo.log('item on click ');
2400 //if(this.preventDefault){
2401 // e.preventDefault();
2403 //this.parent().hideMenuItems();
2405 this.fireEvent('click', this, e);
2424 * @class Roo.bootstrap.MenuSeparator
2425 * @extends Roo.bootstrap.Component
2426 * Bootstrap MenuSeparator class
2429 * Create a new MenuItem
2430 * @param {Object} config The config object
2434 Roo.bootstrap.MenuSeparator = function(config){
2435 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2438 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2440 getAutoCreate : function(){
2459 * @class Roo.bootstrap.Modal
2460 * @extends Roo.bootstrap.Component
2461 * Bootstrap Modal class
2462 * @cfg {String} title Title of dialog
2463 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2464 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2465 * @cfg {Boolean} specificTitle default false
2466 * @cfg {Array} buttons Array of buttons or standard button set..
2467 * @cfg {String} buttonPosition (left|right|center) default right
2468 * @cfg {Boolean} animate default true
2469 * @cfg {Boolean} allow_close default true
2470 * @cfg {Boolean} fitwindow default false
2471 * @cfg {String} size (sm|lg) default empty
2475 * Create a new Modal Dialog
2476 * @param {Object} config The config object
2479 Roo.bootstrap.Modal = function(config){
2480 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2485 * The raw btnclick event for the button
2486 * @param {Roo.EventObject} e
2490 this.buttons = this.buttons || [];
2493 this.tmpl = Roo.factory(this.tmpl);
2498 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2500 title : 'test dialog',
2510 specificTitle: false,
2512 buttonPosition: 'right',
2531 onRender : function(ct, position)
2533 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2536 var cfg = Roo.apply({}, this.getAutoCreate());
2539 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2541 //if (!cfg.name.length) {
2545 cfg.cls += ' ' + this.cls;
2548 cfg.style = this.style;
2550 this.el = Roo.get(document.body).createChild(cfg, position);
2552 //var type = this.el.dom.type;
2555 if(this.tabIndex !== undefined){
2556 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2559 this.dialogEl = this.el.select('.modal-dialog',true).first();
2560 this.bodyEl = this.el.select('.modal-body',true).first();
2561 this.closeEl = this.el.select('.modal-header .close', true).first();
2562 this.footerEl = this.el.select('.modal-footer',true).first();
2563 this.titleEl = this.el.select('.modal-title',true).first();
2567 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2568 this.maskEl.enableDisplayMode("block");
2570 //this.el.addClass("x-dlg-modal");
2572 if (this.buttons.length) {
2573 Roo.each(this.buttons, function(bb) {
2574 var b = Roo.apply({}, bb);
2575 b.xns = b.xns || Roo.bootstrap;
2576 b.xtype = b.xtype || 'Button';
2577 if (typeof(b.listeners) == 'undefined') {
2578 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2581 var btn = Roo.factory(b);
2583 btn.render(this.el.select('.modal-footer div').first());
2587 // render the children.
2590 if(typeof(this.items) != 'undefined'){
2591 var items = this.items;
2594 for(var i =0;i < items.length;i++) {
2595 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2599 this.items = nitems;
2601 // where are these used - they used to be body/close/footer
2605 //this.el.addClass([this.fieldClass, this.cls]);
2609 getAutoCreate : function(){
2614 html : this.html || ''
2619 cls : 'modal-title',
2623 if(this.specificTitle){
2629 if (this.allow_close) {
2641 if(this.size.length){
2642 size = 'modal-' + this.size;
2647 style : 'display: none',
2650 cls: "modal-dialog " + size,
2653 cls : "modal-content",
2656 cls : 'modal-header',
2661 cls : 'modal-footer',
2665 cls: 'btn-' + this.buttonPosition
2682 modal.cls += ' fade';
2688 getChildContainer : function() {
2693 getButtonContainer : function() {
2694 return this.el.select('.modal-footer div',true).first();
2697 initEvents : function()
2699 if (this.allow_close) {
2700 this.closeEl.on('click', this.hide, this);
2702 Roo.EventManager.onWindowResize(this.resize, this, true);
2709 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2710 if (this.fitwindow) {
2711 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2712 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2717 setSize : function(w,h)
2727 if (!this.rendered) {
2731 this.el.setStyle('display', 'block');
2733 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2736 this.el.addClass('in');
2739 this.el.addClass('in');
2743 // not sure how we can show data in here..
2745 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2748 Roo.get(document.body).addClass("x-body-masked");
2749 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2751 this.el.setStyle('zIndex', '10001');
2753 this.fireEvent('show', this);
2758 this.items.forEach( function(e) {
2759 e.layout ? e.layout() : false;
2768 Roo.get(document.body).removeClass("x-body-masked");
2769 this.el.removeClass('in');
2770 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2772 if(this.animate){ // why
2774 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2776 this.el.setStyle('display', 'none');
2779 this.fireEvent('hide', this);
2782 addButton : function(str, cb)
2786 var b = Roo.apply({}, { html : str } );
2787 b.xns = b.xns || Roo.bootstrap;
2788 b.xtype = b.xtype || 'Button';
2789 if (typeof(b.listeners) == 'undefined') {
2790 b.listeners = { click : cb.createDelegate(this) };
2793 var btn = Roo.factory(b);
2795 btn.render(this.el.select('.modal-footer div').first());
2801 setDefaultButton : function(btn)
2803 //this.el.select('.modal-footer').()
2807 resizeTo: function(w,h)
2811 this.dialogEl.setWidth(w);
2812 if (this.diff === false) {
2813 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2816 this.bodyEl.setHeight(h-this.diff);
2820 setContentSize : function(w, h)
2824 onButtonClick: function(btn,e)
2827 this.fireEvent('btnclick', btn.name, e);
2830 * Set the title of the Dialog
2831 * @param {String} str new Title
2833 setTitle: function(str) {
2834 this.titleEl.dom.innerHTML = str;
2837 * Set the body of the Dialog
2838 * @param {String} str new Title
2840 setBody: function(str) {
2841 this.bodyEl.dom.innerHTML = str;
2844 * Set the body of the Dialog using the template
2845 * @param {Obj} data - apply this data to the template and replace the body contents.
2847 applyBody: function(obj)
2850 Roo.log("Error - using apply Body without a template");
2853 this.tmpl.overwrite(this.bodyEl, obj);
2859 Roo.apply(Roo.bootstrap.Modal, {
2861 * Button config that displays a single OK button
2870 * Button config that displays Yes and No buttons
2886 * Button config that displays OK and Cancel buttons
2901 * Button config that displays Yes, No and Cancel buttons
2924 * messagebox - can be used as a replace
2928 * @class Roo.MessageBox
2929 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2933 Roo.Msg.alert('Status', 'Changes saved successfully.');
2935 // Prompt for user data:
2936 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2938 // process text value...
2942 // Show a dialog using config options:
2944 title:'Save Changes?',
2945 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2946 buttons: Roo.Msg.YESNOCANCEL,
2953 Roo.bootstrap.MessageBox = function(){
2954 var dlg, opt, mask, waitTimer;
2955 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2956 var buttons, activeTextEl, bwidth;
2960 var handleButton = function(button){
2962 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2966 var handleHide = function(){
2968 dlg.el.removeClass(opt.cls);
2971 // Roo.TaskMgr.stop(waitTimer);
2972 // waitTimer = null;
2977 var updateButtons = function(b){
2980 buttons["ok"].hide();
2981 buttons["cancel"].hide();
2982 buttons["yes"].hide();
2983 buttons["no"].hide();
2984 //dlg.footer.dom.style.display = 'none';
2987 dlg.footerEl.dom.style.display = '';
2988 for(var k in buttons){
2989 if(typeof buttons[k] != "function"){
2992 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2993 width += buttons[k].el.getWidth()+15;
3003 var handleEsc = function(d, k, e){
3004 if(opt && opt.closable !== false){
3014 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3015 * @return {Roo.BasicDialog} The BasicDialog element
3017 getDialog : function(){
3019 dlg = new Roo.bootstrap.Modal( {
3022 //constraintoviewport:false,
3024 //collapsible : false,
3029 //buttonAlign:"center",
3030 closeClick : function(){
3031 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3034 handleButton("cancel");
3039 dlg.on("hide", handleHide);
3041 //dlg.addKeyListener(27, handleEsc);
3043 this.buttons = buttons;
3044 var bt = this.buttonText;
3045 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3046 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3047 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3048 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3050 bodyEl = dlg.bodyEl.createChild({
3052 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3053 '<textarea class="roo-mb-textarea"></textarea>' +
3054 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3056 msgEl = bodyEl.dom.firstChild;
3057 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3058 textboxEl.enableDisplayMode();
3059 textboxEl.addKeyListener([10,13], function(){
3060 if(dlg.isVisible() && opt && opt.buttons){
3063 }else if(opt.buttons.yes){
3064 handleButton("yes");
3068 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3069 textareaEl.enableDisplayMode();
3070 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3071 progressEl.enableDisplayMode();
3072 var pf = progressEl.dom.firstChild;
3074 pp = Roo.get(pf.firstChild);
3075 pp.setHeight(pf.offsetHeight);
3083 * Updates the message box body text
3084 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3085 * the XHTML-compliant non-breaking space character '&#160;')
3086 * @return {Roo.MessageBox} This message box
3088 updateText : function(text){
3089 if(!dlg.isVisible() && !opt.width){
3090 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3092 msgEl.innerHTML = text || ' ';
3094 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3095 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3097 Math.min(opt.width || cw , this.maxWidth),
3098 Math.max(opt.minWidth || this.minWidth, bwidth)
3101 activeTextEl.setWidth(w);
3103 if(dlg.isVisible()){
3104 dlg.fixedcenter = false;
3106 // to big, make it scroll. = But as usual stupid IE does not support
3109 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3110 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3111 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3113 bodyEl.dom.style.height = '';
3114 bodyEl.dom.style.overflowY = '';
3117 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3119 bodyEl.dom.style.overflowX = '';
3122 dlg.setContentSize(w, bodyEl.getHeight());
3123 if(dlg.isVisible()){
3124 dlg.fixedcenter = true;
3130 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3131 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3132 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3133 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3134 * @return {Roo.MessageBox} This message box
3136 updateProgress : function(value, text){
3138 this.updateText(text);
3140 if (pp) { // weird bug on my firefox - for some reason this is not defined
3141 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3147 * Returns true if the message box is currently displayed
3148 * @return {Boolean} True if the message box is visible, else false
3150 isVisible : function(){
3151 return dlg && dlg.isVisible();
3155 * Hides the message box if it is displayed
3158 if(this.isVisible()){
3164 * Displays a new message box, or reinitializes an existing message box, based on the config options
3165 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3166 * The following config object properties are supported:
3168 Property Type Description
3169 ---------- --------------- ------------------------------------------------------------------------------------
3170 animEl String/Element An id or Element from which the message box should animate as it opens and
3171 closes (defaults to undefined)
3172 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3173 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3174 closable Boolean False to hide the top-right close button (defaults to true). Note that
3175 progress and wait dialogs will ignore this property and always hide the
3176 close button as they can only be closed programmatically.
3177 cls String A custom CSS class to apply to the message box element
3178 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3179 displayed (defaults to 75)
3180 fn Function A callback function to execute after closing the dialog. The arguments to the
3181 function will be btn (the name of the button that was clicked, if applicable,
3182 e.g. "ok"), and text (the value of the active text field, if applicable).
3183 Progress and wait dialogs will ignore this option since they do not respond to
3184 user actions and can only be closed programmatically, so any required function
3185 should be called by the same code after it closes the dialog.
3186 icon String A CSS class that provides a background image to be used as an icon for
3187 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3188 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3189 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3190 modal Boolean False to allow user interaction with the page while the message box is
3191 displayed (defaults to true)
3192 msg String A string that will replace the existing message box body text (defaults
3193 to the XHTML-compliant non-breaking space character ' ')
3194 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3195 progress Boolean True to display a progress bar (defaults to false)
3196 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3197 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3198 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3199 title String The title text
3200 value String The string value to set into the active textbox element if displayed
3201 wait Boolean True to display a progress bar (defaults to false)
3202 width Number The width of the dialog in pixels
3209 msg: 'Please enter your address:',
3211 buttons: Roo.MessageBox.OKCANCEL,
3214 animEl: 'addAddressBtn'
3217 * @param {Object} config Configuration options
3218 * @return {Roo.MessageBox} This message box
3220 show : function(options)
3223 // this causes nightmares if you show one dialog after another
3224 // especially on callbacks..
3226 if(this.isVisible()){
3229 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3230 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3231 Roo.log("New Dialog Message:" + options.msg )
3232 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3233 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3236 var d = this.getDialog();
3238 d.setTitle(opt.title || " ");
3239 d.closeEl.setDisplayed(opt.closable !== false);
3240 activeTextEl = textboxEl;
3241 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3246 textareaEl.setHeight(typeof opt.multiline == "number" ?
3247 opt.multiline : this.defaultTextHeight);
3248 activeTextEl = textareaEl;
3257 progressEl.setDisplayed(opt.progress === true);
3258 this.updateProgress(0);
3259 activeTextEl.dom.value = opt.value || "";
3261 dlg.setDefaultButton(activeTextEl);
3263 var bs = opt.buttons;
3267 }else if(bs && bs.yes){
3268 db = buttons["yes"];
3270 dlg.setDefaultButton(db);
3272 bwidth = updateButtons(opt.buttons);
3273 this.updateText(opt.msg);
3275 d.el.addClass(opt.cls);
3277 d.proxyDrag = opt.proxyDrag === true;
3278 d.modal = opt.modal !== false;
3279 d.mask = opt.modal !== false ? mask : false;
3281 // force it to the end of the z-index stack so it gets a cursor in FF
3282 document.body.appendChild(dlg.el.dom);
3283 d.animateTarget = null;
3284 d.show(options.animEl);
3290 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3291 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3292 * and closing the message box when the process is complete.
3293 * @param {String} title The title bar text
3294 * @param {String} msg The message box body text
3295 * @return {Roo.MessageBox} This message box
3297 progress : function(title, msg){
3304 minWidth: this.minProgressWidth,
3311 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3312 * If a callback function is passed it will be called after the user clicks the button, and the
3313 * id of the button that was clicked will be passed as the only parameter to the callback
3314 * (could also be the top-right close button).
3315 * @param {String} title The title bar text
3316 * @param {String} msg The message box body text
3317 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3318 * @param {Object} scope (optional) The scope of the callback function
3319 * @return {Roo.MessageBox} This message box
3321 alert : function(title, msg, fn, scope){
3334 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3335 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3336 * You are responsible for closing the message box when the process is complete.
3337 * @param {String} msg The message box body text
3338 * @param {String} title (optional) The title bar text
3339 * @return {Roo.MessageBox} This message box
3341 wait : function(msg, title){
3352 waitTimer = Roo.TaskMgr.start({
3354 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3362 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3363 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3364 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3365 * @param {String} title The title bar text
3366 * @param {String} msg The message box body text
3367 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3368 * @param {Object} scope (optional) The scope of the callback function
3369 * @return {Roo.MessageBox} This message box
3371 confirm : function(title, msg, fn, scope){
3375 buttons: this.YESNO,
3384 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3385 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3386 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3387 * (could also be the top-right close button) and the text that was entered will be passed as the two
3388 * parameters to the callback.
3389 * @param {String} title The title bar text
3390 * @param {String} msg The message box body text
3391 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3392 * @param {Object} scope (optional) The scope of the callback function
3393 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3394 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3395 * @return {Roo.MessageBox} This message box
3397 prompt : function(title, msg, fn, scope, multiline){
3401 buttons: this.OKCANCEL,
3406 multiline: multiline,
3413 * Button config that displays a single OK button
3418 * Button config that displays Yes and No buttons
3421 YESNO : {yes:true, no:true},
3423 * Button config that displays OK and Cancel buttons
3426 OKCANCEL : {ok:true, cancel:true},
3428 * Button config that displays Yes, No and Cancel buttons
3431 YESNOCANCEL : {yes:true, no:true, cancel:true},
3434 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3437 defaultTextHeight : 75,
3439 * The maximum width in pixels of the message box (defaults to 600)
3444 * The minimum width in pixels of the message box (defaults to 100)
3449 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3450 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3453 minProgressWidth : 250,
3455 * An object containing the default button text strings that can be overriden for localized language support.
3456 * Supported properties are: ok, cancel, yes and no.
3457 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3470 * Shorthand for {@link Roo.MessageBox}
3472 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3473 Roo.Msg = Roo.Msg || Roo.MessageBox;
3482 * @class Roo.bootstrap.Navbar
3483 * @extends Roo.bootstrap.Component
3484 * Bootstrap Navbar class
3487 * Create a new Navbar
3488 * @param {Object} config The config object
3492 Roo.bootstrap.Navbar = function(config){
3493 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3497 * @event beforetoggle
3498 * Fire before toggle the menu
3499 * @param {Roo.EventObject} e
3501 "beforetoggle" : true
3505 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3514 getAutoCreate : function(){
3517 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3521 initEvents :function ()
3523 //Roo.log(this.el.select('.navbar-toggle',true));
3524 this.el.select('.navbar-toggle',true).on('click', function() {
3525 if(this.fireEvent('beforetoggle', this) !== false){
3526 this.el.select('.navbar-collapse',true).toggleClass('in');
3536 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3538 var size = this.el.getSize();
3539 this.maskEl.setSize(size.width, size.height);
3540 this.maskEl.enableDisplayMode("block");
3549 getChildContainer : function()
3551 if (this.el.select('.collapse').getCount()) {
3552 return this.el.select('.collapse',true).first();
3585 * @class Roo.bootstrap.NavSimplebar
3586 * @extends Roo.bootstrap.Navbar
3587 * Bootstrap Sidebar class
3589 * @cfg {Boolean} inverse is inverted color
3591 * @cfg {String} type (nav | pills | tabs)
3592 * @cfg {Boolean} arrangement stacked | justified
3593 * @cfg {String} align (left | right) alignment
3595 * @cfg {Boolean} main (true|false) main nav bar? default false
3596 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3598 * @cfg {String} tag (header|footer|nav|div) default is nav
3604 * Create a new Sidebar
3605 * @param {Object} config The config object
3609 Roo.bootstrap.NavSimplebar = function(config){
3610 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3613 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3629 getAutoCreate : function(){
3633 tag : this.tag || 'div',
3646 this.type = this.type || 'nav';
3647 if (['tabs','pills'].indexOf(this.type)!==-1) {
3648 cfg.cn[0].cls += ' nav-' + this.type
3652 if (this.type!=='nav') {
3653 Roo.log('nav type must be nav/tabs/pills')
3655 cfg.cn[0].cls += ' navbar-nav'
3661 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3662 cfg.cn[0].cls += ' nav-' + this.arrangement;
3666 if (this.align === 'right') {
3667 cfg.cn[0].cls += ' navbar-right';
3671 cfg.cls += ' navbar-inverse';
3698 * @class Roo.bootstrap.NavHeaderbar
3699 * @extends Roo.bootstrap.NavSimplebar
3700 * Bootstrap Sidebar class
3702 * @cfg {String} brand what is brand
3703 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3704 * @cfg {String} brand_href href of the brand
3705 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3706 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3707 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3708 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3711 * Create a new Sidebar
3712 * @param {Object} config The config object
3716 Roo.bootstrap.NavHeaderbar = function(config){
3717 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3721 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3728 desktopCenter : false,
3731 getAutoCreate : function(){
3734 tag: this.nav || 'nav',
3741 if (this.desktopCenter) {
3742 cn.push({cls : 'container', cn : []});
3749 cls: 'navbar-header',
3754 cls: 'navbar-toggle',
3755 'data-toggle': 'collapse',
3760 html: 'Toggle navigation'
3782 cls: 'collapse navbar-collapse',
3786 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3788 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3789 cfg.cls += ' navbar-' + this.position;
3791 // tag can override this..
3793 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3796 if (this.brand !== '') {
3799 href: this.brand_href ? this.brand_href : '#',
3800 cls: 'navbar-brand',
3808 cfg.cls += ' main-nav';
3816 getHeaderChildContainer : function()
3818 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3819 return this.el.select('.navbar-header',true).first();
3822 return this.getChildContainer();
3826 initEvents : function()
3828 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3830 if (this.autohide) {
3835 Roo.get(document).on('scroll',function(e) {
3836 var ns = Roo.get(document).getScroll().top;
3837 var os = prevScroll;
3841 ft.removeClass('slideDown');
3842 ft.addClass('slideUp');
3845 ft.removeClass('slideUp');
3846 ft.addClass('slideDown');
3867 * @class Roo.bootstrap.NavSidebar
3868 * @extends Roo.bootstrap.Navbar
3869 * Bootstrap Sidebar class
3872 * Create a new Sidebar
3873 * @param {Object} config The config object
3877 Roo.bootstrap.NavSidebar = function(config){
3878 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3881 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3883 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3885 getAutoCreate : function(){
3890 cls: 'sidebar sidebar-nav'
3912 * @class Roo.bootstrap.NavGroup
3913 * @extends Roo.bootstrap.Component
3914 * Bootstrap NavGroup class
3915 * @cfg {String} align (left|right)
3916 * @cfg {Boolean} inverse
3917 * @cfg {String} type (nav|pills|tab) default nav
3918 * @cfg {String} navId - reference Id for navbar.
3922 * Create a new nav group
3923 * @param {Object} config The config object
3926 Roo.bootstrap.NavGroup = function(config){
3927 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3930 Roo.bootstrap.NavGroup.register(this);
3934 * Fires when the active item changes
3935 * @param {Roo.bootstrap.NavGroup} this
3936 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3937 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3944 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3955 getAutoCreate : function()
3957 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3964 if (['tabs','pills'].indexOf(this.type)!==-1) {
3965 cfg.cls += ' nav-' + this.type
3967 if (this.type!=='nav') {
3968 Roo.log('nav type must be nav/tabs/pills')
3970 cfg.cls += ' navbar-nav'
3973 if (this.parent().sidebar) {
3976 cls: 'dashboard-menu sidebar-menu'
3982 if (this.form === true) {
3988 if (this.align === 'right') {
3989 cfg.cls += ' navbar-right';
3991 cfg.cls += ' navbar-left';
3995 if (this.align === 'right') {
3996 cfg.cls += ' navbar-right';
4000 cfg.cls += ' navbar-inverse';
4008 * sets the active Navigation item
4009 * @param {Roo.bootstrap.NavItem} the new current navitem
4011 setActiveItem : function(item)
4014 Roo.each(this.navItems, function(v){
4019 v.setActive(false, true);
4026 item.setActive(true, true);
4027 this.fireEvent('changed', this, item, prev);
4032 * gets the active Navigation item
4033 * @return {Roo.bootstrap.NavItem} the current navitem
4035 getActive : function()
4039 Roo.each(this.navItems, function(v){
4050 indexOfNav : function()
4054 Roo.each(this.navItems, function(v,i){
4065 * adds a Navigation item
4066 * @param {Roo.bootstrap.NavItem} the navitem to add
4068 addItem : function(cfg)
4070 var cn = new Roo.bootstrap.NavItem(cfg);
4072 cn.parentId = this.id;
4073 cn.onRender(this.el, null);
4077 * register a Navigation item
4078 * @param {Roo.bootstrap.NavItem} the navitem to add
4080 register : function(item)
4082 this.navItems.push( item);
4083 item.navId = this.navId;
4088 * clear all the Navigation item
4091 clearAll : function()
4094 this.el.dom.innerHTML = '';
4097 getNavItem: function(tabId)
4100 Roo.each(this.navItems, function(e) {
4101 if (e.tabId == tabId) {
4111 setActiveNext : function()
4113 var i = this.indexOfNav(this.getActive());
4114 if (i > this.navItems.length) {
4117 this.setActiveItem(this.navItems[i+1]);
4119 setActivePrev : function()
4121 var i = this.indexOfNav(this.getActive());
4125 this.setActiveItem(this.navItems[i-1]);
4127 clearWasActive : function(except) {
4128 Roo.each(this.navItems, function(e) {
4129 if (e.tabId != except.tabId && e.was_active) {
4130 e.was_active = false;
4137 getWasActive : function ()
4140 Roo.each(this.navItems, function(e) {
4155 Roo.apply(Roo.bootstrap.NavGroup, {
4159 * register a Navigation Group
4160 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4162 register : function(navgrp)
4164 this.groups[navgrp.navId] = navgrp;
4168 * fetch a Navigation Group based on the navigation ID
4169 * @param {string} the navgroup to add
4170 * @returns {Roo.bootstrap.NavGroup} the navgroup
4172 get: function(navId) {
4173 if (typeof(this.groups[navId]) == 'undefined') {
4175 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4177 return this.groups[navId] ;
4192 * @class Roo.bootstrap.NavItem
4193 * @extends Roo.bootstrap.Component
4194 * Bootstrap Navbar.NavItem class
4195 * @cfg {String} href link to
4196 * @cfg {String} html content of button
4197 * @cfg {String} badge text inside badge
4198 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4199 * @cfg {String} glyphicon name of glyphicon
4200 * @cfg {String} icon name of font awesome icon
4201 * @cfg {Boolean} active Is item active
4202 * @cfg {Boolean} disabled Is item disabled
4204 * @cfg {Boolean} preventDefault (true | false) default false
4205 * @cfg {String} tabId the tab that this item activates.
4206 * @cfg {String} tagtype (a|span) render as a href or span?
4207 * @cfg {Boolean} animateRef (true|false) link to element default false
4210 * Create a new Navbar Item
4211 * @param {Object} config The config object
4213 Roo.bootstrap.NavItem = function(config){
4214 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4219 * The raw click event for the entire grid.
4220 * @param {Roo.EventObject} e
4225 * Fires when the active item active state changes
4226 * @param {Roo.bootstrap.NavItem} this
4227 * @param {boolean} state the new state
4233 * Fires when scroll to element
4234 * @param {Roo.bootstrap.NavItem} this
4235 * @param {Object} options
4236 * @param {Roo.EventObject} e
4244 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4252 preventDefault : false,
4259 getAutoCreate : function(){
4268 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4270 if (this.disabled) {
4271 cfg.cls += ' disabled';
4274 if (this.href || this.html || this.glyphicon || this.icon) {
4278 href : this.href || "#",
4279 html: this.html || ''
4284 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4287 if(this.glyphicon) {
4288 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4293 cfg.cn[0].html += " <span class='caret'></span>";
4297 if (this.badge !== '') {
4299 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4307 initEvents: function()
4309 if (typeof (this.menu) != 'undefined') {
4310 this.menu.parentType = this.xtype;
4311 this.menu.triggerEl = this.el;
4312 this.menu = this.addxtype(Roo.apply({}, this.menu));
4315 this.el.select('a',true).on('click', this.onClick, this);
4317 if(this.tagtype == 'span'){
4318 this.el.select('span',true).on('click', this.onClick, this);
4321 // at this point parent should be available..
4322 this.parent().register(this);
4325 onClick : function(e)
4327 if (e.getTarget('.dropdown-menu-item')) {
4328 // did you click on a menu itemm.... - then don't trigger onclick..
4333 this.preventDefault ||
4336 Roo.log("NavItem - prevent Default?");
4340 if (this.disabled) {
4344 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4345 if (tg && tg.transition) {
4346 Roo.log("waiting for the transitionend");
4352 //Roo.log("fire event clicked");
4353 if(this.fireEvent('click', this, e) === false){
4357 if(this.tagtype == 'span'){
4361 //Roo.log(this.href);
4362 var ael = this.el.select('a',true).first();
4365 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4366 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4367 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4368 return; // ignore... - it's a 'hash' to another page.
4370 Roo.log("NavItem - prevent Default?");
4372 this.scrollToElement(e);
4376 var p = this.parent();
4378 if (['tabs','pills'].indexOf(p.type)!==-1) {
4379 if (typeof(p.setActiveItem) !== 'undefined') {
4380 p.setActiveItem(this);
4384 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4385 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4386 // remove the collapsed menu expand...
4387 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4391 isActive: function () {
4394 setActive : function(state, fire, is_was_active)
4396 if (this.active && !state && this.navId) {
4397 this.was_active = true;
4398 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4400 nv.clearWasActive(this);
4404 this.active = state;
4407 this.el.removeClass('active');
4408 } else if (!this.el.hasClass('active')) {
4409 this.el.addClass('active');
4412 this.fireEvent('changed', this, state);
4415 // show a panel if it's registered and related..
4417 if (!this.navId || !this.tabId || !state || is_was_active) {
4421 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4425 var pan = tg.getPanelByName(this.tabId);
4429 // if we can not flip to new panel - go back to old nav highlight..
4430 if (false == tg.showPanel(pan)) {
4431 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4433 var onav = nv.getWasActive();
4435 onav.setActive(true, false, true);
4444 // this should not be here...
4445 setDisabled : function(state)
4447 this.disabled = state;
4449 this.el.removeClass('disabled');
4450 } else if (!this.el.hasClass('disabled')) {
4451 this.el.addClass('disabled');
4457 * Fetch the element to display the tooltip on.
4458 * @return {Roo.Element} defaults to this.el
4460 tooltipEl : function()
4462 return this.el.select('' + this.tagtype + '', true).first();
4465 scrollToElement : function(e)
4467 var c = document.body;
4470 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4472 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4473 c = document.documentElement;
4476 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4482 var o = target.calcOffsetsTo(c);
4489 this.fireEvent('scrollto', this, options, e);
4491 Roo.get(c).scrollTo('top', options.value, true);
4504 * <span> icon </span>
4505 * <span> text </span>
4506 * <span>badge </span>
4510 * @class Roo.bootstrap.NavSidebarItem
4511 * @extends Roo.bootstrap.NavItem
4512 * Bootstrap Navbar.NavSidebarItem class
4513 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4514 * {bool} open is the menu open
4516 * Create a new Navbar Button
4517 * @param {Object} config The config object
4519 Roo.bootstrap.NavSidebarItem = function(config){
4520 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4525 * The raw click event for the entire grid.
4526 * @param {Roo.EventObject} e
4531 * Fires when the active item active state changes
4532 * @param {Roo.bootstrap.NavSidebarItem} this
4533 * @param {boolean} state the new state
4541 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4543 badgeWeight : 'default',
4547 getAutoCreate : function(){
4552 href : this.href || '#',
4564 html : this.html || ''
4569 cfg.cls += ' active';
4572 if (this.disabled) {
4573 cfg.cls += ' disabled';
4576 cfg.cls += ' open x-open';
4579 if (this.glyphicon || this.icon) {
4580 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4581 a.cn.push({ tag : 'i', cls : c }) ;
4586 if (this.badge !== '') {
4588 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4592 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4593 a.cls += 'dropdown-toggle treeview' ;
4601 initEvents : function()
4603 if (typeof (this.menu) != 'undefined') {
4604 this.menu.parentType = this.xtype;
4605 this.menu.triggerEl = this.el;
4606 this.menu = this.addxtype(Roo.apply({}, this.menu));
4609 this.el.on('click', this.onClick, this);
4612 if(this.badge !== ''){
4614 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4619 onClick : function(e)
4626 if(this.preventDefault){
4630 this.fireEvent('click', this);
4633 disable : function()
4635 this.setDisabled(true);
4640 this.setDisabled(false);
4643 setDisabled : function(state)
4645 if(this.disabled == state){
4649 this.disabled = state;
4652 this.el.addClass('disabled');
4656 this.el.removeClass('disabled');
4661 setActive : function(state)
4663 if(this.active == state){
4667 this.active = state;
4670 this.el.addClass('active');
4674 this.el.removeClass('active');
4679 isActive: function ()
4684 setBadge : function(str)
4690 this.badgeEl.dom.innerHTML = str;
4707 * @class Roo.bootstrap.Row
4708 * @extends Roo.bootstrap.Component
4709 * Bootstrap Row class (contains columns...)
4713 * @param {Object} config The config object
4716 Roo.bootstrap.Row = function(config){
4717 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4720 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4722 getAutoCreate : function(){
4741 * @class Roo.bootstrap.Element
4742 * @extends Roo.bootstrap.Component
4743 * Bootstrap Element class
4744 * @cfg {String} html contents of the element
4745 * @cfg {String} tag tag of the element
4746 * @cfg {String} cls class of the element
4747 * @cfg {Boolean} preventDefault (true|false) default false
4748 * @cfg {Boolean} clickable (true|false) default false
4751 * Create a new Element
4752 * @param {Object} config The config object
4755 Roo.bootstrap.Element = function(config){
4756 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4762 * When a element is chick
4763 * @param {Roo.bootstrap.Element} this
4764 * @param {Roo.EventObject} e
4770 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4775 preventDefault: false,
4778 getAutoCreate : function(){
4789 initEvents: function()
4791 Roo.bootstrap.Element.superclass.initEvents.call(this);
4794 this.el.on('click', this.onClick, this);
4799 onClick : function(e)
4801 if(this.preventDefault){
4805 this.fireEvent('click', this, e);
4808 getValue : function()
4810 return this.el.dom.innerHTML;
4813 setValue : function(value)
4815 this.el.dom.innerHTML = value;
4830 * @class Roo.bootstrap.Pagination
4831 * @extends Roo.bootstrap.Component
4832 * Bootstrap Pagination class
4833 * @cfg {String} size xs | sm | md | lg
4834 * @cfg {Boolean} inverse false | true
4837 * Create a new Pagination
4838 * @param {Object} config The config object
4841 Roo.bootstrap.Pagination = function(config){
4842 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4845 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4851 getAutoCreate : function(){
4857 cfg.cls += ' inverse';
4863 cfg.cls += " " + this.cls;
4881 * @class Roo.bootstrap.PaginationItem
4882 * @extends Roo.bootstrap.Component
4883 * Bootstrap PaginationItem class
4884 * @cfg {String} html text
4885 * @cfg {String} href the link
4886 * @cfg {Boolean} preventDefault (true | false) default true
4887 * @cfg {Boolean} active (true | false) default false
4888 * @cfg {Boolean} disabled default false
4892 * Create a new PaginationItem
4893 * @param {Object} config The config object
4897 Roo.bootstrap.PaginationItem = function(config){
4898 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4903 * The raw click event for the entire grid.
4904 * @param {Roo.EventObject} e
4910 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4914 preventDefault: true,
4919 getAutoCreate : function(){
4925 href : this.href ? this.href : '#',
4926 html : this.html ? this.html : ''
4936 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4940 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4946 initEvents: function() {
4948 this.el.on('click', this.onClick, this);
4951 onClick : function(e)
4953 Roo.log('PaginationItem on click ');
4954 if(this.preventDefault){
4962 this.fireEvent('click', this, e);
4978 * @class Roo.bootstrap.Slider
4979 * @extends Roo.bootstrap.Component
4980 * Bootstrap Slider class
4983 * Create a new Slider
4984 * @param {Object} config The config object
4987 Roo.bootstrap.Slider = function(config){
4988 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4991 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4993 getAutoCreate : function(){
4997 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5001 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5013 * Ext JS Library 1.1.1
5014 * Copyright(c) 2006-2007, Ext JS, LLC.
5016 * Originally Released Under LGPL - original licence link has changed is not relivant.
5019 * <script type="text/javascript">
5024 * @class Roo.grid.ColumnModel
5025 * @extends Roo.util.Observable
5026 * This is the default implementation of a ColumnModel used by the Grid. It defines
5027 * the columns in the grid.
5030 var colModel = new Roo.grid.ColumnModel([
5031 {header: "Ticker", width: 60, sortable: true, locked: true},
5032 {header: "Company Name", width: 150, sortable: true},
5033 {header: "Market Cap.", width: 100, sortable: true},
5034 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5035 {header: "Employees", width: 100, sortable: true, resizable: false}
5040 * The config options listed for this class are options which may appear in each
5041 * individual column definition.
5042 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5044 * @param {Object} config An Array of column config objects. See this class's
5045 * config objects for details.
5047 Roo.grid.ColumnModel = function(config){
5049 * The config passed into the constructor
5051 this.config = config;
5054 // if no id, create one
5055 // if the column does not have a dataIndex mapping,
5056 // map it to the order it is in the config
5057 for(var i = 0, len = config.length; i < len; i++){
5059 if(typeof c.dataIndex == "undefined"){
5062 if(typeof c.renderer == "string"){
5063 c.renderer = Roo.util.Format[c.renderer];
5065 if(typeof c.id == "undefined"){
5068 if(c.editor && c.editor.xtype){
5069 c.editor = Roo.factory(c.editor, Roo.grid);
5071 if(c.editor && c.editor.isFormField){
5072 c.editor = new Roo.grid.GridEditor(c.editor);
5074 this.lookup[c.id] = c;
5078 * The width of columns which have no width specified (defaults to 100)
5081 this.defaultWidth = 100;
5084 * Default sortable of columns which have no sortable specified (defaults to false)
5087 this.defaultSortable = false;
5091 * @event widthchange
5092 * Fires when the width of a column changes.
5093 * @param {ColumnModel} this
5094 * @param {Number} columnIndex The column index
5095 * @param {Number} newWidth The new width
5097 "widthchange": true,
5099 * @event headerchange
5100 * Fires when the text of a header changes.
5101 * @param {ColumnModel} this
5102 * @param {Number} columnIndex The column index
5103 * @param {Number} newText The new header text
5105 "headerchange": true,
5107 * @event hiddenchange
5108 * Fires when a column is hidden or "unhidden".
5109 * @param {ColumnModel} this
5110 * @param {Number} columnIndex The column index
5111 * @param {Boolean} hidden true if hidden, false otherwise
5113 "hiddenchange": true,
5115 * @event columnmoved
5116 * Fires when a column is moved.
5117 * @param {ColumnModel} this
5118 * @param {Number} oldIndex
5119 * @param {Number} newIndex
5121 "columnmoved" : true,
5123 * @event columlockchange
5124 * Fires when a column's locked state is changed
5125 * @param {ColumnModel} this
5126 * @param {Number} colIndex
5127 * @param {Boolean} locked true if locked
5129 "columnlockchange" : true
5131 Roo.grid.ColumnModel.superclass.constructor.call(this);
5133 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5135 * @cfg {String} header The header text to display in the Grid view.
5138 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5139 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5140 * specified, the column's index is used as an index into the Record's data Array.
5143 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5144 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5147 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5148 * Defaults to the value of the {@link #defaultSortable} property.
5149 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5152 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5155 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5158 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5161 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5164 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5165 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5166 * default renderer uses the raw data value. If an object is returned (bootstrap only)
5167 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5170 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5173 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5176 * @cfg {String} cursor (Optional)
5179 * @cfg {String} tooltip (Optional)
5182 * @cfg {Number} xs (Optional)
5185 * @cfg {Number} sm (Optional)
5188 * @cfg {Number} md (Optional)
5191 * @cfg {Number} lg (Optional)
5194 * Returns the id of the column at the specified index.
5195 * @param {Number} index The column index
5196 * @return {String} the id
5198 getColumnId : function(index){
5199 return this.config[index].id;
5203 * Returns the column for a specified id.
5204 * @param {String} id The column id
5205 * @return {Object} the column
5207 getColumnById : function(id){
5208 return this.lookup[id];
5213 * Returns the column for a specified dataIndex.
5214 * @param {String} dataIndex The column dataIndex
5215 * @return {Object|Boolean} the column or false if not found
5217 getColumnByDataIndex: function(dataIndex){
5218 var index = this.findColumnIndex(dataIndex);
5219 return index > -1 ? this.config[index] : false;
5223 * Returns the index for a specified column id.
5224 * @param {String} id The column id
5225 * @return {Number} the index, or -1 if not found
5227 getIndexById : function(id){
5228 for(var i = 0, len = this.config.length; i < len; i++){
5229 if(this.config[i].id == id){
5237 * Returns the index for a specified column dataIndex.
5238 * @param {String} dataIndex The column dataIndex
5239 * @return {Number} the index, or -1 if not found
5242 findColumnIndex : function(dataIndex){
5243 for(var i = 0, len = this.config.length; i < len; i++){
5244 if(this.config[i].dataIndex == dataIndex){
5252 moveColumn : function(oldIndex, newIndex){
5253 var c = this.config[oldIndex];
5254 this.config.splice(oldIndex, 1);
5255 this.config.splice(newIndex, 0, c);
5256 this.dataMap = null;
5257 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5260 isLocked : function(colIndex){
5261 return this.config[colIndex].locked === true;
5264 setLocked : function(colIndex, value, suppressEvent){
5265 if(this.isLocked(colIndex) == value){
5268 this.config[colIndex].locked = value;
5270 this.fireEvent("columnlockchange", this, colIndex, value);
5274 getTotalLockedWidth : function(){
5276 for(var i = 0; i < this.config.length; i++){
5277 if(this.isLocked(i) && !this.isHidden(i)){
5278 this.totalWidth += this.getColumnWidth(i);
5284 getLockedCount : function(){
5285 for(var i = 0, len = this.config.length; i < len; i++){
5286 if(!this.isLocked(i)){
5291 return this.config.length;
5295 * Returns the number of columns.
5298 getColumnCount : function(visibleOnly){
5299 if(visibleOnly === true){
5301 for(var i = 0, len = this.config.length; i < len; i++){
5302 if(!this.isHidden(i)){
5308 return this.config.length;
5312 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5313 * @param {Function} fn
5314 * @param {Object} scope (optional)
5315 * @return {Array} result
5317 getColumnsBy : function(fn, scope){
5319 for(var i = 0, len = this.config.length; i < len; i++){
5320 var c = this.config[i];
5321 if(fn.call(scope||this, c, i) === true){
5329 * Returns true if the specified column is sortable.
5330 * @param {Number} col The column index
5333 isSortable : function(col){
5334 if(typeof this.config[col].sortable == "undefined"){
5335 return this.defaultSortable;
5337 return this.config[col].sortable;
5341 * Returns the rendering (formatting) function defined for the column.
5342 * @param {Number} col The column index.
5343 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5345 getRenderer : function(col){
5346 if(!this.config[col].renderer){
5347 return Roo.grid.ColumnModel.defaultRenderer;
5349 return this.config[col].renderer;
5353 * Sets the rendering (formatting) function for a column.
5354 * @param {Number} col The column index
5355 * @param {Function} fn The function to use to process the cell's raw data
5356 * to return HTML markup for the grid view. The render function is called with
5357 * the following parameters:<ul>
5358 * <li>Data value.</li>
5359 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5360 * <li>css A CSS style string to apply to the table cell.</li>
5361 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5362 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5363 * <li>Row index</li>
5364 * <li>Column index</li>
5365 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5367 setRenderer : function(col, fn){
5368 this.config[col].renderer = fn;
5372 * Returns the width for the specified column.
5373 * @param {Number} col The column index
5376 getColumnWidth : function(col){
5377 return this.config[col].width * 1 || this.defaultWidth;
5381 * Sets the width for a column.
5382 * @param {Number} col The column index
5383 * @param {Number} width The new width
5385 setColumnWidth : function(col, width, suppressEvent){
5386 this.config[col].width = width;
5387 this.totalWidth = null;
5389 this.fireEvent("widthchange", this, col, width);
5394 * Returns the total width of all columns.
5395 * @param {Boolean} includeHidden True to include hidden column widths
5398 getTotalWidth : function(includeHidden){
5399 if(!this.totalWidth){
5400 this.totalWidth = 0;
5401 for(var i = 0, len = this.config.length; i < len; i++){
5402 if(includeHidden || !this.isHidden(i)){
5403 this.totalWidth += this.getColumnWidth(i);
5407 return this.totalWidth;
5411 * Returns the header for the specified column.
5412 * @param {Number} col The column index
5415 getColumnHeader : function(col){
5416 return this.config[col].header;
5420 * Sets the header for a column.
5421 * @param {Number} col The column index
5422 * @param {String} header The new header
5424 setColumnHeader : function(col, header){
5425 this.config[col].header = header;
5426 this.fireEvent("headerchange", this, col, header);
5430 * Returns the tooltip for the specified column.
5431 * @param {Number} col The column index
5434 getColumnTooltip : function(col){
5435 return this.config[col].tooltip;
5438 * Sets the tooltip for a column.
5439 * @param {Number} col The column index
5440 * @param {String} tooltip The new tooltip
5442 setColumnTooltip : function(col, tooltip){
5443 this.config[col].tooltip = tooltip;
5447 * Returns the dataIndex for the specified column.
5448 * @param {Number} col The column index
5451 getDataIndex : function(col){
5452 return this.config[col].dataIndex;
5456 * Sets the dataIndex for a column.
5457 * @param {Number} col The column index
5458 * @param {Number} dataIndex The new dataIndex
5460 setDataIndex : function(col, dataIndex){
5461 this.config[col].dataIndex = dataIndex;
5467 * Returns true if the cell is editable.
5468 * @param {Number} colIndex The column index
5469 * @param {Number} rowIndex The row index - this is nto actually used..?
5472 isCellEditable : function(colIndex, rowIndex){
5473 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5477 * Returns the editor defined for the cell/column.
5478 * return false or null to disable editing.
5479 * @param {Number} colIndex The column index
5480 * @param {Number} rowIndex The row index
5483 getCellEditor : function(colIndex, rowIndex){
5484 return this.config[colIndex].editor;
5488 * Sets if a column is editable.
5489 * @param {Number} col The column index
5490 * @param {Boolean} editable True if the column is editable
5492 setEditable : function(col, editable){
5493 this.config[col].editable = editable;
5498 * Returns true if the column is hidden.
5499 * @param {Number} colIndex The column index
5502 isHidden : function(colIndex){
5503 return this.config[colIndex].hidden;
5508 * Returns true if the column width cannot be changed
5510 isFixed : function(colIndex){
5511 return this.config[colIndex].fixed;
5515 * Returns true if the column can be resized
5518 isResizable : function(colIndex){
5519 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5522 * Sets if a column is hidden.
5523 * @param {Number} colIndex The column index
5524 * @param {Boolean} hidden True if the column is hidden
5526 setHidden : function(colIndex, hidden){
5527 this.config[colIndex].hidden = hidden;
5528 this.totalWidth = null;
5529 this.fireEvent("hiddenchange", this, colIndex, hidden);
5533 * Sets the editor for a column.
5534 * @param {Number} col The column index
5535 * @param {Object} editor The editor object
5537 setEditor : function(col, editor){
5538 this.config[col].editor = editor;
5542 Roo.grid.ColumnModel.defaultRenderer = function(value){
5543 if(typeof value == "string" && value.length < 1){
5549 // Alias for backwards compatibility
5550 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5553 * Ext JS Library 1.1.1
5554 * Copyright(c) 2006-2007, Ext JS, LLC.
5556 * Originally Released Under LGPL - original licence link has changed is not relivant.
5559 * <script type="text/javascript">
5563 * @class Roo.LoadMask
5564 * A simple utility class for generically masking elements while loading data. If the element being masked has
5565 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5566 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5567 * element's UpdateManager load indicator and will be destroyed after the initial load.
5569 * Create a new LoadMask
5570 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5571 * @param {Object} config The config object
5573 Roo.LoadMask = function(el, config){
5574 this.el = Roo.get(el);
5575 Roo.apply(this, config);
5577 this.store.on('beforeload', this.onBeforeLoad, this);
5578 this.store.on('load', this.onLoad, this);
5579 this.store.on('loadexception', this.onLoadException, this);
5580 this.removeMask = false;
5582 var um = this.el.getUpdateManager();
5583 um.showLoadIndicator = false; // disable the default indicator
5584 um.on('beforeupdate', this.onBeforeLoad, this);
5585 um.on('update', this.onLoad, this);
5586 um.on('failure', this.onLoad, this);
5587 this.removeMask = true;
5591 Roo.LoadMask.prototype = {
5593 * @cfg {Boolean} removeMask
5594 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5595 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5599 * The text to display in a centered loading message box (defaults to 'Loading...')
5603 * @cfg {String} msgCls
5604 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5606 msgCls : 'x-mask-loading',
5609 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5615 * Disables the mask to prevent it from being displayed
5617 disable : function(){
5618 this.disabled = true;
5622 * Enables the mask so that it can be displayed
5624 enable : function(){
5625 this.disabled = false;
5628 onLoadException : function()
5632 if (typeof(arguments[3]) != 'undefined') {
5633 Roo.MessageBox.alert("Error loading",arguments[3]);
5637 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5638 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5647 this.el.unmask(this.removeMask);
5652 this.el.unmask(this.removeMask);
5656 onBeforeLoad : function(){
5658 this.el.mask(this.msg, this.msgCls);
5663 destroy : function(){
5665 this.store.un('beforeload', this.onBeforeLoad, this);
5666 this.store.un('load', this.onLoad, this);
5667 this.store.un('loadexception', this.onLoadException, this);
5669 var um = this.el.getUpdateManager();
5670 um.un('beforeupdate', this.onBeforeLoad, this);
5671 um.un('update', this.onLoad, this);
5672 um.un('failure', this.onLoad, this);
5683 * @class Roo.bootstrap.Table
5684 * @extends Roo.bootstrap.Component
5685 * Bootstrap Table class
5686 * @cfg {String} cls table class
5687 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5688 * @cfg {String} bgcolor Specifies the background color for a table
5689 * @cfg {Number} border Specifies whether the table cells should have borders or not
5690 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5691 * @cfg {Number} cellspacing Specifies the space between cells
5692 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5693 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5694 * @cfg {String} sortable Specifies that the table should be sortable
5695 * @cfg {String} summary Specifies a summary of the content of a table
5696 * @cfg {Number} width Specifies the width of a table
5697 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5699 * @cfg {boolean} striped Should the rows be alternative striped
5700 * @cfg {boolean} bordered Add borders to the table
5701 * @cfg {boolean} hover Add hover highlighting
5702 * @cfg {boolean} condensed Format condensed
5703 * @cfg {boolean} responsive Format condensed
5704 * @cfg {Boolean} loadMask (true|false) default false
5705 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5706 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5707 * @cfg {Boolean} rowSelection (true|false) default false
5708 * @cfg {Boolean} cellSelection (true|false) default false
5709 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5710 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5714 * Create a new Table
5715 * @param {Object} config The config object
5718 Roo.bootstrap.Table = function(config){
5719 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5724 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5725 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5726 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5727 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5729 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5731 this.sm.grid = this;
5732 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5733 this.sm = this.selModel;
5734 this.sm.xmodule = this.xmodule || false;
5737 if (this.cm && typeof(this.cm.config) == 'undefined') {
5738 this.colModel = new Roo.grid.ColumnModel(this.cm);
5739 this.cm = this.colModel;
5740 this.cm.xmodule = this.xmodule || false;
5743 this.store= Roo.factory(this.store, Roo.data);
5744 this.ds = this.store;
5745 this.ds.xmodule = this.xmodule || false;
5748 if (this.footer && this.store) {
5749 this.footer.dataSource = this.ds;
5750 this.footer = Roo.factory(this.footer);
5757 * Fires when a cell is clicked
5758 * @param {Roo.bootstrap.Table} this
5759 * @param {Roo.Element} el
5760 * @param {Number} rowIndex
5761 * @param {Number} columnIndex
5762 * @param {Roo.EventObject} e
5766 * @event celldblclick
5767 * Fires when a cell is double clicked
5768 * @param {Roo.bootstrap.Table} this
5769 * @param {Roo.Element} el
5770 * @param {Number} rowIndex
5771 * @param {Number} columnIndex
5772 * @param {Roo.EventObject} e
5774 "celldblclick" : true,
5777 * Fires when a row is clicked
5778 * @param {Roo.bootstrap.Table} this
5779 * @param {Roo.Element} el
5780 * @param {Number} rowIndex
5781 * @param {Roo.EventObject} e
5785 * @event rowdblclick
5786 * Fires when a row is double clicked
5787 * @param {Roo.bootstrap.Table} this
5788 * @param {Roo.Element} el
5789 * @param {Number} rowIndex
5790 * @param {Roo.EventObject} e
5792 "rowdblclick" : true,
5795 * Fires when a mouseover occur
5796 * @param {Roo.bootstrap.Table} this
5797 * @param {Roo.Element} el
5798 * @param {Number} rowIndex
5799 * @param {Number} columnIndex
5800 * @param {Roo.EventObject} e
5805 * Fires when a mouseout occur
5806 * @param {Roo.bootstrap.Table} this
5807 * @param {Roo.Element} el
5808 * @param {Number} rowIndex
5809 * @param {Number} columnIndex
5810 * @param {Roo.EventObject} e
5815 * Fires when a row is rendered, so you can change add a style to it.
5816 * @param {Roo.bootstrap.Table} this
5817 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5821 * @event rowsrendered
5822 * Fires when all the rows have been rendered
5823 * @param {Roo.bootstrap.Table} this
5825 'rowsrendered' : true,
5827 * @event contextmenu
5828 * The raw contextmenu event for the entire grid.
5829 * @param {Roo.EventObject} e
5831 "contextmenu" : true,
5833 * @event rowcontextmenu
5834 * Fires when a row is right clicked
5835 * @param {Roo.bootstrap.Table} this
5836 * @param {Number} rowIndex
5837 * @param {Roo.EventObject} e
5839 "rowcontextmenu" : true,
5841 * @event cellcontextmenu
5842 * Fires when a cell is right clicked
5843 * @param {Roo.bootstrap.Table} this
5844 * @param {Number} rowIndex
5845 * @param {Number} cellIndex
5846 * @param {Roo.EventObject} e
5848 "cellcontextmenu" : true,
5850 * @event headercontextmenu
5851 * Fires when a header is right clicked
5852 * @param {Roo.bootstrap.Table} this
5853 * @param {Number} columnIndex
5854 * @param {Roo.EventObject} e
5856 "headercontextmenu" : true
5860 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5886 rowSelection : false,
5887 cellSelection : false,
5890 // Roo.Element - the tbody
5892 // Roo.Element - thead element
5895 container: false, // used by gridpanel...
5897 getAutoCreate : function()
5899 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5906 if (this.scrollBody) {
5907 cfg.cls += ' table-body-fixed';
5910 cfg.cls += ' table-striped';
5914 cfg.cls += ' table-hover';
5916 if (this.bordered) {
5917 cfg.cls += ' table-bordered';
5919 if (this.condensed) {
5920 cfg.cls += ' table-condensed';
5922 if (this.responsive) {
5923 cfg.cls += ' table-responsive';
5927 cfg.cls+= ' ' +this.cls;
5930 // this lot should be simplifed...
5933 cfg.align=this.align;
5936 cfg.bgcolor=this.bgcolor;
5939 cfg.border=this.border;
5941 if (this.cellpadding) {
5942 cfg.cellpadding=this.cellpadding;
5944 if (this.cellspacing) {
5945 cfg.cellspacing=this.cellspacing;
5948 cfg.frame=this.frame;
5951 cfg.rules=this.rules;
5953 if (this.sortable) {
5954 cfg.sortable=this.sortable;
5957 cfg.summary=this.summary;
5960 cfg.width=this.width;
5963 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5966 if(this.store || this.cm){
5967 if(this.headerShow){
5968 cfg.cn.push(this.renderHeader());
5971 cfg.cn.push(this.renderBody());
5973 if(this.footerShow){
5974 cfg.cn.push(this.renderFooter());
5976 // where does this come from?
5977 //cfg.cls+= ' TableGrid';
5980 return { cn : [ cfg ] };
5983 initEvents : function()
5985 if(!this.store || !this.cm){
5988 if (this.selModel) {
5989 this.selModel.initEvents();
5993 //Roo.log('initEvents with ds!!!!');
5995 this.mainBody = this.el.select('tbody', true).first();
5996 this.mainHead = this.el.select('thead', true).first();
6003 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6004 e.on('click', _this.sort, _this);
6007 this.el.on("click", this.onClick, this);
6008 this.el.on("dblclick", this.onDblClick, this);
6010 // why is this done????? = it breaks dialogs??
6011 //this.parent().el.setStyle('position', 'relative');
6015 this.footer.parentId = this.id;
6016 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6019 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6021 this.store.on('load', this.onLoad, this);
6022 this.store.on('beforeload', this.onBeforeLoad, this);
6023 this.store.on('update', this.onUpdate, this);
6024 this.store.on('add', this.onAdd, this);
6025 this.store.on("clear", this.clear, this);
6027 this.el.on("contextmenu", this.onContextMenu, this);
6029 this.mainBody.on('scroll', this.onBodyScroll, this);
6034 onContextMenu : function(e, t)
6036 this.processEvent("contextmenu", e);
6039 processEvent : function(name, e)
6041 if (name != 'touchstart' ) {
6042 this.fireEvent(name, e);
6045 var t = e.getTarget();
6047 var cell = Roo.get(t);
6053 if(cell.findParent('tfoot', false, true)){
6057 if(cell.findParent('thead', false, true)){
6059 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6060 cell = Roo.get(t).findParent('th', false, true);
6062 Roo.log("failed to find th in thead?");
6063 Roo.log(e.getTarget());
6068 var cellIndex = cell.dom.cellIndex;
6070 var ename = name == 'touchstart' ? 'click' : name;
6071 this.fireEvent("header" + ename, this, cellIndex, e);
6076 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6077 cell = Roo.get(t).findParent('td', false, true);
6079 Roo.log("failed to find th in tbody?");
6080 Roo.log(e.getTarget());
6085 var row = cell.findParent('tr', false, true);
6086 var cellIndex = cell.dom.cellIndex;
6087 var rowIndex = row.dom.rowIndex - 1;
6091 this.fireEvent("row" + name, this, rowIndex, e);
6095 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6101 onMouseover : function(e, el)
6103 var cell = Roo.get(el);
6109 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6110 cell = cell.findParent('td', false, true);
6113 var row = cell.findParent('tr', false, true);
6114 var cellIndex = cell.dom.cellIndex;
6115 var rowIndex = row.dom.rowIndex - 1; // start from 0
6117 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6121 onMouseout : function(e, el)
6123 var cell = Roo.get(el);
6129 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6130 cell = cell.findParent('td', false, true);
6133 var row = cell.findParent('tr', false, true);
6134 var cellIndex = cell.dom.cellIndex;
6135 var rowIndex = row.dom.rowIndex - 1; // start from 0
6137 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6141 onClick : function(e, el)
6143 var cell = Roo.get(el);
6145 if(!cell || (!this.cellSelection && !this.rowSelection)){
6149 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6150 cell = cell.findParent('td', false, true);
6153 if(!cell || typeof(cell) == 'undefined'){
6157 var row = cell.findParent('tr', false, true);
6159 if(!row || typeof(row) == 'undefined'){
6163 var cellIndex = cell.dom.cellIndex;
6164 var rowIndex = this.getRowIndex(row);
6166 // why??? - should these not be based on SelectionModel?
6167 if(this.cellSelection){
6168 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6171 if(this.rowSelection){
6172 this.fireEvent('rowclick', this, row, rowIndex, e);
6178 onDblClick : function(e,el)
6180 var cell = Roo.get(el);
6182 if(!cell || (!this.cellSelection && !this.rowSelection)){
6186 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6187 cell = cell.findParent('td', false, true);
6190 if(!cell || typeof(cell) == 'undefined'){
6194 var row = cell.findParent('tr', false, true);
6196 if(!row || typeof(row) == 'undefined'){
6200 var cellIndex = cell.dom.cellIndex;
6201 var rowIndex = this.getRowIndex(row);
6203 if(this.cellSelection){
6204 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6207 if(this.rowSelection){
6208 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6212 sort : function(e,el)
6214 var col = Roo.get(el);
6216 if(!col.hasClass('sortable')){
6220 var sort = col.attr('sort');
6223 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6227 this.store.sortInfo = {field : sort, direction : dir};
6230 Roo.log("calling footer first");
6231 this.footer.onClick('first');
6234 this.store.load({ params : { start : 0 } });
6238 renderHeader : function()
6246 this.totalWidth = 0;
6248 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6250 var config = cm.config[i];
6255 html: cm.getColumnHeader(i)
6260 if(typeof(config.sortable) != 'undefined' && config.sortable){
6262 c.html = '<i class="glyphicon"></i>' + c.html;
6265 if(typeof(config.lgHeader) != 'undefined'){
6266 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6269 if(typeof(config.mdHeader) != 'undefined'){
6270 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6273 if(typeof(config.smHeader) != 'undefined'){
6274 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6277 if(typeof(config.xsHeader) != 'undefined'){
6278 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6285 if(typeof(config.tooltip) != 'undefined'){
6286 c.tooltip = config.tooltip;
6289 if(typeof(config.colspan) != 'undefined'){
6290 c.colspan = config.colspan;
6293 if(typeof(config.hidden) != 'undefined' && config.hidden){
6294 c.style += ' display:none;';
6297 if(typeof(config.dataIndex) != 'undefined'){
6298 c.sort = config.dataIndex;
6303 if(typeof(config.align) != 'undefined' && config.align.length){
6304 c.style += ' text-align:' + config.align + ';';
6307 if(typeof(config.width) != 'undefined'){
6308 c.style += ' width:' + config.width + 'px;';
6309 this.totalWidth += config.width;
6311 this.totalWidth += 100; // assume minimum of 100 per column?
6314 if(typeof(config.cls) != 'undefined'){
6315 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6318 ['xs','sm','md','lg'].map(function(size){
6320 if(typeof(config[size]) == 'undefined'){
6324 if (!config[size]) { // 0 = hidden
6325 c.cls += ' hidden-' + size;
6329 c.cls += ' col-' + size + '-' + config[size];
6339 renderBody : function()
6349 colspan : this.cm.getColumnCount()
6359 renderFooter : function()
6369 colspan : this.cm.getColumnCount()
6383 // Roo.log('ds onload');
6388 var ds = this.store;
6390 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6391 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6392 if (_this.store.sortInfo) {
6394 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6395 e.select('i', true).addClass(['glyphicon-arrow-up']);
6398 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6399 e.select('i', true).addClass(['glyphicon-arrow-down']);
6404 var tbody = this.mainBody;
6406 if(ds.getCount() > 0){
6407 ds.data.each(function(d,rowIndex){
6408 var row = this.renderRow(cm, ds, rowIndex);
6410 tbody.createChild(row);
6414 if(row.cellObjects.length){
6415 Roo.each(row.cellObjects, function(r){
6416 _this.renderCellObject(r);
6423 Roo.each(this.el.select('tbody td', true).elements, function(e){
6424 e.on('mouseover', _this.onMouseover, _this);
6427 Roo.each(this.el.select('tbody td', true).elements, function(e){
6428 e.on('mouseout', _this.onMouseout, _this);
6430 this.fireEvent('rowsrendered', this);
6431 //if(this.loadMask){
6432 // this.maskEl.hide();
6439 onUpdate : function(ds,record)
6441 this.refreshRow(record);
6445 onRemove : function(ds, record, index, isUpdate){
6446 if(isUpdate !== true){
6447 this.fireEvent("beforerowremoved", this, index, record);
6449 var bt = this.mainBody.dom;
6451 var rows = this.el.select('tbody > tr', true).elements;
6453 if(typeof(rows[index]) != 'undefined'){
6454 bt.removeChild(rows[index].dom);
6457 // if(bt.rows[index]){
6458 // bt.removeChild(bt.rows[index]);
6461 if(isUpdate !== true){
6462 //this.stripeRows(index);
6463 //this.syncRowHeights(index, index);
6465 this.fireEvent("rowremoved", this, index, record);
6469 onAdd : function(ds, records, rowIndex)
6471 //Roo.log('on Add called');
6472 // - note this does not handle multiple adding very well..
6473 var bt = this.mainBody.dom;
6474 for (var i =0 ; i < records.length;i++) {
6475 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6476 //Roo.log(records[i]);
6477 //Roo.log(this.store.getAt(rowIndex+i));
6478 this.insertRow(this.store, rowIndex + i, false);
6485 refreshRow : function(record){
6486 var ds = this.store, index;
6487 if(typeof record == 'number'){
6489 record = ds.getAt(index);
6491 index = ds.indexOf(record);
6493 this.insertRow(ds, index, true);
6495 this.onRemove(ds, record, index+1, true);
6497 //this.syncRowHeights(index, index);
6499 this.fireEvent("rowupdated", this, index, record);
6502 insertRow : function(dm, rowIndex, isUpdate){
6505 this.fireEvent("beforerowsinserted", this, rowIndex);
6507 //var s = this.getScrollState();
6508 var row = this.renderRow(this.cm, this.store, rowIndex);
6509 // insert before rowIndex..
6510 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6514 if(row.cellObjects.length){
6515 Roo.each(row.cellObjects, function(r){
6516 _this.renderCellObject(r);
6521 this.fireEvent("rowsinserted", this, rowIndex);
6522 //this.syncRowHeights(firstRow, lastRow);
6523 //this.stripeRows(firstRow);
6530 getRowDom : function(rowIndex)
6532 var rows = this.el.select('tbody > tr', true).elements;
6534 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6537 // returns the object tree for a tr..
6540 renderRow : function(cm, ds, rowIndex)
6543 var d = ds.getAt(rowIndex);
6550 var cellObjects = [];
6552 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6553 var config = cm.config[i];
6555 var renderer = cm.getRenderer(i);
6559 if(typeof(renderer) !== 'undefined'){
6560 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6562 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6563 // and are rendered into the cells after the row is rendered - using the id for the element.
6565 if(typeof(value) === 'object'){
6575 rowIndex : rowIndex,
6580 this.fireEvent('rowclass', this, rowcfg);
6584 cls : rowcfg.rowClass,
6586 html: (typeof(value) === 'object') ? '' : value
6593 if(typeof(config.colspan) != 'undefined'){
6594 td.colspan = config.colspan;
6597 if(typeof(config.hidden) != 'undefined' && config.hidden){
6598 td.style += ' display:none;';
6601 if(typeof(config.align) != 'undefined' && config.align.length){
6602 td.style += ' text-align:' + config.align + ';';
6605 if(typeof(config.width) != 'undefined'){
6606 td.style += ' width:' + config.width + 'px;';
6609 if(typeof(config.cursor) != 'undefined'){
6610 td.style += ' cursor:' + config.cursor + ';';
6613 if(typeof(config.cls) != 'undefined'){
6614 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6617 ['xs','sm','md','lg'].map(function(size){
6619 if(typeof(config[size]) == 'undefined'){
6623 if (!config[size]) { // 0 = hidden
6624 td.cls += ' hidden-' + size;
6628 td.cls += ' col-' + size + '-' + config[size];
6636 row.cellObjects = cellObjects;
6644 onBeforeLoad : function()
6646 //Roo.log('ds onBeforeLoad');
6650 //if(this.loadMask){
6651 // this.maskEl.show();
6659 this.el.select('tbody', true).first().dom.innerHTML = '';
6662 * Show or hide a row.
6663 * @param {Number} rowIndex to show or hide
6664 * @param {Boolean} state hide
6666 setRowVisibility : function(rowIndex, state)
6668 var bt = this.mainBody.dom;
6670 var rows = this.el.select('tbody > tr', true).elements;
6672 if(typeof(rows[rowIndex]) == 'undefined'){
6675 rows[rowIndex].dom.style.display = state ? '' : 'none';
6679 getSelectionModel : function(){
6681 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6683 return this.selModel;
6686 * Render the Roo.bootstrap object from renderder
6688 renderCellObject : function(r)
6692 var t = r.cfg.render(r.container);
6695 Roo.each(r.cfg.cn, function(c){
6697 container: t.getChildContainer(),
6700 _this.renderCellObject(child);
6705 getRowIndex : function(row)
6709 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6720 * Returns the grid's underlying element = used by panel.Grid
6721 * @return {Element} The element
6723 getGridEl : function(){
6727 * Forces a resize - used by panel.Grid
6728 * @return {Element} The element
6730 autoSize : function()
6732 //var ctr = Roo.get(this.container.dom.parentElement);
6733 var ctr = Roo.get(this.el.dom);
6735 var thd = this.getGridEl().select('thead',true).first();
6736 var tbd = this.getGridEl().select('tbody', true).first();
6739 var cw = ctr.getWidth();
6743 tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6744 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6747 cw = Math.max(cw, this.totalWidth);
6748 this.getGridEl().select('tr',true).setWidth(cw);
6749 // resize 'expandable coloumn?
6751 return; // we doe not have a view in this design..
6754 onBodyScroll: function()
6757 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6758 this.mainHead.setStyle({
6759 'position' : 'relative',
6760 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6777 * @class Roo.bootstrap.TableCell
6778 * @extends Roo.bootstrap.Component
6779 * Bootstrap TableCell class
6780 * @cfg {String} html cell contain text
6781 * @cfg {String} cls cell class
6782 * @cfg {String} tag cell tag (td|th) default td
6783 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6784 * @cfg {String} align Aligns the content in a cell
6785 * @cfg {String} axis Categorizes cells
6786 * @cfg {String} bgcolor Specifies the background color of a cell
6787 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6788 * @cfg {Number} colspan Specifies the number of columns a cell should span
6789 * @cfg {String} headers Specifies one or more header cells a cell is related to
6790 * @cfg {Number} height Sets the height of a cell
6791 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6792 * @cfg {Number} rowspan Sets the number of rows a cell should span
6793 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6794 * @cfg {String} valign Vertical aligns the content in a cell
6795 * @cfg {Number} width Specifies the width of a cell
6798 * Create a new TableCell
6799 * @param {Object} config The config object
6802 Roo.bootstrap.TableCell = function(config){
6803 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6806 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6826 getAutoCreate : function(){
6827 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6847 cfg.align=this.align
6853 cfg.bgcolor=this.bgcolor
6856 cfg.charoff=this.charoff
6859 cfg.colspan=this.colspan
6862 cfg.headers=this.headers
6865 cfg.height=this.height
6868 cfg.nowrap=this.nowrap
6871 cfg.rowspan=this.rowspan
6874 cfg.scope=this.scope
6877 cfg.valign=this.valign
6880 cfg.width=this.width
6899 * @class Roo.bootstrap.TableRow
6900 * @extends Roo.bootstrap.Component
6901 * Bootstrap TableRow class
6902 * @cfg {String} cls row class
6903 * @cfg {String} align Aligns the content in a table row
6904 * @cfg {String} bgcolor Specifies a background color for a table row
6905 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6906 * @cfg {String} valign Vertical aligns the content in a table row
6909 * Create a new TableRow
6910 * @param {Object} config The config object
6913 Roo.bootstrap.TableRow = function(config){
6914 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6917 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6925 getAutoCreate : function(){
6926 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6936 cfg.align = this.align;
6939 cfg.bgcolor = this.bgcolor;
6942 cfg.charoff = this.charoff;
6945 cfg.valign = this.valign;
6963 * @class Roo.bootstrap.TableBody
6964 * @extends Roo.bootstrap.Component
6965 * Bootstrap TableBody class
6966 * @cfg {String} cls element class
6967 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6968 * @cfg {String} align Aligns the content inside the element
6969 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6970 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6973 * Create a new TableBody
6974 * @param {Object} config The config object
6977 Roo.bootstrap.TableBody = function(config){
6978 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6981 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6989 getAutoCreate : function(){
6990 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7004 cfg.align = this.align;
7007 cfg.charoff = this.charoff;
7010 cfg.valign = this.valign;
7017 // initEvents : function()
7024 // this.store = Roo.factory(this.store, Roo.data);
7025 // this.store.on('load', this.onLoad, this);
7027 // this.store.load();
7031 // onLoad: function ()
7033 // this.fireEvent('load', this);
7043 * Ext JS Library 1.1.1
7044 * Copyright(c) 2006-2007, Ext JS, LLC.
7046 * Originally Released Under LGPL - original licence link has changed is not relivant.
7049 * <script type="text/javascript">
7052 // as we use this in bootstrap.
7053 Roo.namespace('Roo.form');
7055 * @class Roo.form.Action
7056 * Internal Class used to handle form actions
7058 * @param {Roo.form.BasicForm} el The form element or its id
7059 * @param {Object} config Configuration options
7064 // define the action interface
7065 Roo.form.Action = function(form, options){
7067 this.options = options || {};
7070 * Client Validation Failed
7073 Roo.form.Action.CLIENT_INVALID = 'client';
7075 * Server Validation Failed
7078 Roo.form.Action.SERVER_INVALID = 'server';
7080 * Connect to Server Failed
7083 Roo.form.Action.CONNECT_FAILURE = 'connect';
7085 * Reading Data from Server Failed
7088 Roo.form.Action.LOAD_FAILURE = 'load';
7090 Roo.form.Action.prototype = {
7092 failureType : undefined,
7093 response : undefined,
7097 run : function(options){
7102 success : function(response){
7107 handleResponse : function(response){
7111 // default connection failure
7112 failure : function(response){
7114 this.response = response;
7115 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7116 this.form.afterAction(this, false);
7119 processResponse : function(response){
7120 this.response = response;
7121 if(!response.responseText){
7124 this.result = this.handleResponse(response);
7128 // utility functions used internally
7129 getUrl : function(appendParams){
7130 var url = this.options.url || this.form.url || this.form.el.dom.action;
7132 var p = this.getParams();
7134 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7140 getMethod : function(){
7141 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7144 getParams : function(){
7145 var bp = this.form.baseParams;
7146 var p = this.options.params;
7148 if(typeof p == "object"){
7149 p = Roo.urlEncode(Roo.applyIf(p, bp));
7150 }else if(typeof p == 'string' && bp){
7151 p += '&' + Roo.urlEncode(bp);
7154 p = Roo.urlEncode(bp);
7159 createCallback : function(){
7161 success: this.success,
7162 failure: this.failure,
7164 timeout: (this.form.timeout*1000),
7165 upload: this.form.fileUpload ? this.success : undefined
7170 Roo.form.Action.Submit = function(form, options){
7171 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7174 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7177 haveProgress : false,
7178 uploadComplete : false,
7180 // uploadProgress indicator.
7181 uploadProgress : function()
7183 if (!this.form.progressUrl) {
7187 if (!this.haveProgress) {
7188 Roo.MessageBox.progress("Uploading", "Uploading");
7190 if (this.uploadComplete) {
7191 Roo.MessageBox.hide();
7195 this.haveProgress = true;
7197 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7199 var c = new Roo.data.Connection();
7201 url : this.form.progressUrl,
7206 success : function(req){
7207 //console.log(data);
7211 rdata = Roo.decode(req.responseText)
7213 Roo.log("Invalid data from server..");
7217 if (!rdata || !rdata.success) {
7219 Roo.MessageBox.alert(Roo.encode(rdata));
7222 var data = rdata.data;
7224 if (this.uploadComplete) {
7225 Roo.MessageBox.hide();
7230 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7231 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7234 this.uploadProgress.defer(2000,this);
7237 failure: function(data) {
7238 Roo.log('progress url failed ');
7249 // run get Values on the form, so it syncs any secondary forms.
7250 this.form.getValues();
7252 var o = this.options;
7253 var method = this.getMethod();
7254 var isPost = method == 'POST';
7255 if(o.clientValidation === false || this.form.isValid()){
7257 if (this.form.progressUrl) {
7258 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7259 (new Date() * 1) + '' + Math.random());
7264 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7265 form:this.form.el.dom,
7266 url:this.getUrl(!isPost),
7268 params:isPost ? this.getParams() : null,
7269 isUpload: this.form.fileUpload
7272 this.uploadProgress();
7274 }else if (o.clientValidation !== false){ // client validation failed
7275 this.failureType = Roo.form.Action.CLIENT_INVALID;
7276 this.form.afterAction(this, false);
7280 success : function(response)
7282 this.uploadComplete= true;
7283 if (this.haveProgress) {
7284 Roo.MessageBox.hide();
7288 var result = this.processResponse(response);
7289 if(result === true || result.success){
7290 this.form.afterAction(this, true);
7294 this.form.markInvalid(result.errors);
7295 this.failureType = Roo.form.Action.SERVER_INVALID;
7297 this.form.afterAction(this, false);
7299 failure : function(response)
7301 this.uploadComplete= true;
7302 if (this.haveProgress) {
7303 Roo.MessageBox.hide();
7306 this.response = response;
7307 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7308 this.form.afterAction(this, false);
7311 handleResponse : function(response){
7312 if(this.form.errorReader){
7313 var rs = this.form.errorReader.read(response);
7316 for(var i = 0, len = rs.records.length; i < len; i++) {
7317 var r = rs.records[i];
7321 if(errors.length < 1){
7325 success : rs.success,
7331 ret = Roo.decode(response.responseText);
7335 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7345 Roo.form.Action.Load = function(form, options){
7346 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7347 this.reader = this.form.reader;
7350 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7355 Roo.Ajax.request(Roo.apply(
7356 this.createCallback(), {
7357 method:this.getMethod(),
7358 url:this.getUrl(false),
7359 params:this.getParams()
7363 success : function(response){
7365 var result = this.processResponse(response);
7366 if(result === true || !result.success || !result.data){
7367 this.failureType = Roo.form.Action.LOAD_FAILURE;
7368 this.form.afterAction(this, false);
7371 this.form.clearInvalid();
7372 this.form.setValues(result.data);
7373 this.form.afterAction(this, true);
7376 handleResponse : function(response){
7377 if(this.form.reader){
7378 var rs = this.form.reader.read(response);
7379 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7381 success : rs.success,
7385 return Roo.decode(response.responseText);
7389 Roo.form.Action.ACTION_TYPES = {
7390 'load' : Roo.form.Action.Load,
7391 'submit' : Roo.form.Action.Submit
7400 * @class Roo.bootstrap.Form
7401 * @extends Roo.bootstrap.Component
7402 * Bootstrap Form class
7403 * @cfg {String} method GET | POST (default POST)
7404 * @cfg {String} labelAlign top | left (default top)
7405 * @cfg {String} align left | right - for navbars
7406 * @cfg {Boolean} loadMask load mask when submit (default true)
7411 * @param {Object} config The config object
7415 Roo.bootstrap.Form = function(config){
7416 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7419 * @event clientvalidation
7420 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7421 * @param {Form} this
7422 * @param {Boolean} valid true if the form has passed client-side validation
7424 clientvalidation: true,
7426 * @event beforeaction
7427 * Fires before any action is performed. Return false to cancel the action.
7428 * @param {Form} this
7429 * @param {Action} action The action to be performed
7433 * @event actionfailed
7434 * Fires when an action fails.
7435 * @param {Form} this
7436 * @param {Action} action The action that failed
7438 actionfailed : true,
7440 * @event actioncomplete
7441 * Fires when an action is completed.
7442 * @param {Form} this
7443 * @param {Action} action The action that completed
7445 actioncomplete : true
7450 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7453 * @cfg {String} method
7454 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7459 * The URL to use for form actions if one isn't supplied in the action options.
7462 * @cfg {Boolean} fileUpload
7463 * Set to true if this form is a file upload.
7467 * @cfg {Object} baseParams
7468 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7472 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7476 * @cfg {Sting} align (left|right) for navbar forms
7481 activeAction : null,
7484 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7485 * element by passing it or its id or mask the form itself by passing in true.
7488 waitMsgTarget : false,
7492 getAutoCreate : function(){
7496 method : this.method || 'POST',
7497 id : this.id || Roo.id(),
7500 if (this.parent().xtype.match(/^Nav/)) {
7501 cfg.cls = 'navbar-form navbar-' + this.align;
7505 if (this.labelAlign == 'left' ) {
7506 cfg.cls += ' form-horizontal';
7512 initEvents : function()
7514 this.el.on('submit', this.onSubmit, this);
7515 // this was added as random key presses on the form where triggering form submit.
7516 this.el.on('keypress', function(e) {
7517 if (e.getCharCode() != 13) {
7520 // we might need to allow it for textareas.. and some other items.
7521 // check e.getTarget().
7523 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7527 Roo.log("keypress blocked");
7535 onSubmit : function(e){
7540 * Returns true if client-side validation on the form is successful.
7543 isValid : function(){
7544 var items = this.getItems();
7546 items.each(function(f){
7555 * Returns true if any fields in this form have changed since their original load.
7558 isDirty : function(){
7560 var items = this.getItems();
7561 items.each(function(f){
7571 * Performs a predefined action (submit or load) or custom actions you define on this form.
7572 * @param {String} actionName The name of the action type
7573 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7574 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7575 * accept other config options):
7577 Property Type Description
7578 ---------------- --------------- ----------------------------------------------------------------------------------
7579 url String The url for the action (defaults to the form's url)
7580 method String The form method to use (defaults to the form's method, or POST if not defined)
7581 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7582 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7583 validate the form on the client (defaults to false)
7585 * @return {BasicForm} this
7587 doAction : function(action, options){
7588 if(typeof action == 'string'){
7589 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7591 if(this.fireEvent('beforeaction', this, action) !== false){
7592 this.beforeAction(action);
7593 action.run.defer(100, action);
7599 beforeAction : function(action){
7600 var o = action.options;
7603 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7605 // not really supported yet.. ??
7607 //if(this.waitMsgTarget === true){
7608 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7609 //}else if(this.waitMsgTarget){
7610 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7611 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7613 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7619 afterAction : function(action, success){
7620 this.activeAction = null;
7621 var o = action.options;
7623 //if(this.waitMsgTarget === true){
7625 //}else if(this.waitMsgTarget){
7626 // this.waitMsgTarget.unmask();
7628 // Roo.MessageBox.updateProgress(1);
7629 // Roo.MessageBox.hide();
7636 Roo.callback(o.success, o.scope, [this, action]);
7637 this.fireEvent('actioncomplete', this, action);
7641 // failure condition..
7642 // we have a scenario where updates need confirming.
7643 // eg. if a locking scenario exists..
7644 // we look for { errors : { needs_confirm : true }} in the response.
7646 (typeof(action.result) != 'undefined') &&
7647 (typeof(action.result.errors) != 'undefined') &&
7648 (typeof(action.result.errors.needs_confirm) != 'undefined')
7651 Roo.log("not supported yet");
7654 Roo.MessageBox.confirm(
7655 "Change requires confirmation",
7656 action.result.errorMsg,
7661 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7671 Roo.callback(o.failure, o.scope, [this, action]);
7672 // show an error message if no failed handler is set..
7673 if (!this.hasListener('actionfailed')) {
7674 Roo.log("need to add dialog support");
7676 Roo.MessageBox.alert("Error",
7677 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7678 action.result.errorMsg :
7679 "Saving Failed, please check your entries or try again"
7684 this.fireEvent('actionfailed', this, action);
7689 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7690 * @param {String} id The value to search for
7693 findField : function(id){
7694 var items = this.getItems();
7695 var field = items.get(id);
7697 items.each(function(f){
7698 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7705 return field || null;
7708 * Mark fields in this form invalid in bulk.
7709 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7710 * @return {BasicForm} this
7712 markInvalid : function(errors){
7713 if(errors instanceof Array){
7714 for(var i = 0, len = errors.length; i < len; i++){
7715 var fieldError = errors[i];
7716 var f = this.findField(fieldError.id);
7718 f.markInvalid(fieldError.msg);
7724 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7725 field.markInvalid(errors[id]);
7729 //Roo.each(this.childForms || [], function (f) {
7730 // f.markInvalid(errors);
7737 * Set values for fields in this form in bulk.
7738 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7739 * @return {BasicForm} this
7741 setValues : function(values){
7742 if(values instanceof Array){ // array of objects
7743 for(var i = 0, len = values.length; i < len; i++){
7745 var f = this.findField(v.id);
7747 f.setValue(v.value);
7748 if(this.trackResetOnLoad){
7749 f.originalValue = f.getValue();
7753 }else{ // object hash
7756 if(typeof values[id] != 'function' && (field = this.findField(id))){
7758 if (field.setFromData &&
7760 field.displayField &&
7761 // combos' with local stores can
7762 // be queried via setValue()
7763 // to set their value..
7764 (field.store && !field.store.isLocal)
7768 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7769 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7770 field.setFromData(sd);
7773 field.setValue(values[id]);
7777 if(this.trackResetOnLoad){
7778 field.originalValue = field.getValue();
7784 //Roo.each(this.childForms || [], function (f) {
7785 // f.setValues(values);
7792 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7793 * they are returned as an array.
7794 * @param {Boolean} asString
7797 getValues : function(asString){
7798 //if (this.childForms) {
7799 // copy values from the child forms
7800 // Roo.each(this.childForms, function (f) {
7801 // this.setValues(f.getValues());
7807 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7808 if(asString === true){
7811 return Roo.urlDecode(fs);
7815 * Returns the fields in this form as an object with key/value pairs.
7816 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7819 getFieldValues : function(with_hidden)
7821 var items = this.getItems();
7823 items.each(function(f){
7827 var v = f.getValue();
7828 if (f.inputType =='radio') {
7829 if (typeof(ret[f.getName()]) == 'undefined') {
7830 ret[f.getName()] = ''; // empty..
7833 if (!f.el.dom.checked) {
7841 // not sure if this supported any more..
7842 if ((typeof(v) == 'object') && f.getRawValue) {
7843 v = f.getRawValue() ; // dates..
7845 // combo boxes where name != hiddenName...
7846 if (f.name != f.getName()) {
7847 ret[f.name] = f.getRawValue();
7849 ret[f.getName()] = v;
7856 * Clears all invalid messages in this form.
7857 * @return {BasicForm} this
7859 clearInvalid : function(){
7860 var items = this.getItems();
7862 items.each(function(f){
7873 * @return {BasicForm} this
7876 var items = this.getItems();
7877 items.each(function(f){
7881 Roo.each(this.childForms || [], function (f) {
7888 getItems : function()
7890 var r=new Roo.util.MixedCollection(false, function(o){
7891 return o.id || (o.id = Roo.id());
7893 var iter = function(el) {
7900 Roo.each(el.items,function(e) {
7920 * Ext JS Library 1.1.1
7921 * Copyright(c) 2006-2007, Ext JS, LLC.
7923 * Originally Released Under LGPL - original licence link has changed is not relivant.
7926 * <script type="text/javascript">
7929 * @class Roo.form.VTypes
7930 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7933 Roo.form.VTypes = function(){
7934 // closure these in so they are only created once.
7935 var alpha = /^[a-zA-Z_]+$/;
7936 var alphanum = /^[a-zA-Z0-9_]+$/;
7937 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7938 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7940 // All these messages and functions are configurable
7943 * The function used to validate email addresses
7944 * @param {String} value The email address
7946 'email' : function(v){
7947 return email.test(v);
7950 * The error text to display when the email validation function returns false
7953 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7955 * The keystroke filter mask to be applied on email input
7958 'emailMask' : /[a-z0-9_\.\-@]/i,
7961 * The function used to validate URLs
7962 * @param {String} value The URL
7964 'url' : function(v){
7968 * The error text to display when the url validation function returns false
7971 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7974 * The function used to validate alpha values
7975 * @param {String} value The value
7977 'alpha' : function(v){
7978 return alpha.test(v);
7981 * The error text to display when the alpha validation function returns false
7984 'alphaText' : 'This field should only contain letters and _',
7986 * The keystroke filter mask to be applied on alpha input
7989 'alphaMask' : /[a-z_]/i,
7992 * The function used to validate alphanumeric values
7993 * @param {String} value The value
7995 'alphanum' : function(v){
7996 return alphanum.test(v);
7999 * The error text to display when the alphanumeric validation function returns false
8002 'alphanumText' : 'This field should only contain letters, numbers and _',
8004 * The keystroke filter mask to be applied on alphanumeric input
8007 'alphanumMask' : /[a-z0-9_]/i
8017 * @class Roo.bootstrap.Input
8018 * @extends Roo.bootstrap.Component
8019 * Bootstrap Input class
8020 * @cfg {Boolean} disabled is it disabled
8021 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8022 * @cfg {String} name name of the input
8023 * @cfg {string} fieldLabel - the label associated
8024 * @cfg {string} placeholder - placeholder to put in text.
8025 * @cfg {string} before - input group add on before
8026 * @cfg {string} after - input group add on after
8027 * @cfg {string} size - (lg|sm) or leave empty..
8028 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8029 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8030 * @cfg {Number} md colspan out of 12 for computer-sized screens
8031 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8032 * @cfg {string} value default value of the input
8033 * @cfg {Number} labelWidth set the width of label (0-12)
8034 * @cfg {String} labelAlign (top|left)
8035 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8036 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8037 * @cfg {String} indicatorpos (left|right) default left
8039 * @cfg {String} align (left|center|right) Default left
8040 * @cfg {Boolean} forceFeedback (true|false) Default false
8046 * Create a new Input
8047 * @param {Object} config The config object
8050 Roo.bootstrap.Input = function(config){
8051 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8056 * Fires when this field receives input focus.
8057 * @param {Roo.form.Field} this
8062 * Fires when this field loses input focus.
8063 * @param {Roo.form.Field} this
8068 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8069 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8070 * @param {Roo.form.Field} this
8071 * @param {Roo.EventObject} e The event object
8076 * Fires just before the field blurs if the field value has changed.
8077 * @param {Roo.form.Field} this
8078 * @param {Mixed} newValue The new value
8079 * @param {Mixed} oldValue The original value
8084 * Fires after the field has been marked as invalid.
8085 * @param {Roo.form.Field} this
8086 * @param {String} msg The validation message
8091 * Fires after the field has been validated with no errors.
8092 * @param {Roo.form.Field} this
8097 * Fires after the key up
8098 * @param {Roo.form.Field} this
8099 * @param {Roo.EventObject} e The event Object
8105 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8107 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8108 automatic validation (defaults to "keyup").
8110 validationEvent : "keyup",
8112 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8114 validateOnBlur : true,
8116 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8118 validationDelay : 250,
8120 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8122 focusClass : "x-form-focus", // not needed???
8126 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8128 invalidClass : "has-warning",
8131 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8133 validClass : "has-success",
8136 * @cfg {Boolean} hasFeedback (true|false) default true
8141 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8143 invalidFeedbackClass : "glyphicon-warning-sign",
8146 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8148 validFeedbackClass : "glyphicon-ok",
8151 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8153 selectOnFocus : false,
8156 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8160 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8165 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8167 disableKeyFilter : false,
8170 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8174 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8178 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8180 blankText : "This field is required",
8183 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8187 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8189 maxLength : Number.MAX_VALUE,
8191 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8193 minLengthText : "The minimum length for this field is {0}",
8195 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8197 maxLengthText : "The maximum length for this field is {0}",
8201 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8202 * If available, this function will be called only after the basic validators all return true, and will be passed the
8203 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8207 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8208 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8209 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8213 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8217 autocomplete: false,
8236 formatedValue : false,
8237 forceFeedback : false,
8239 indicatorpos : 'left',
8241 parentLabelAlign : function()
8244 while (parent.parent()) {
8245 parent = parent.parent();
8246 if (typeof(parent.labelAlign) !='undefined') {
8247 return parent.labelAlign;
8254 getAutoCreate : function()
8256 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8262 if(this.inputType != 'hidden'){
8263 cfg.cls = 'form-group' //input-group
8269 type : this.inputType,
8271 cls : 'form-control',
8272 placeholder : this.placeholder || '',
8273 autocomplete : this.autocomplete || 'new-password'
8277 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8280 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8281 input.maxLength = this.maxLength;
8284 if (this.disabled) {
8285 input.disabled=true;
8288 if (this.readOnly) {
8289 input.readonly=true;
8293 input.name = this.name;
8297 input.cls += ' input-' + this.size;
8301 ['xs','sm','md','lg'].map(function(size){
8302 if (settings[size]) {
8303 cfg.cls += ' col-' + size + '-' + settings[size];
8307 var inputblock = input;
8311 cls: 'glyphicon form-control-feedback'
8314 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8317 cls : 'has-feedback',
8325 if (this.before || this.after) {
8328 cls : 'input-group',
8332 if (this.before && typeof(this.before) == 'string') {
8334 inputblock.cn.push({
8336 cls : 'roo-input-before input-group-addon',
8340 if (this.before && typeof(this.before) == 'object') {
8341 this.before = Roo.factory(this.before);
8343 inputblock.cn.push({
8345 cls : 'roo-input-before input-group-' +
8346 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8350 inputblock.cn.push(input);
8352 if (this.after && typeof(this.after) == 'string') {
8353 inputblock.cn.push({
8355 cls : 'roo-input-after input-group-addon',
8359 if (this.after && typeof(this.after) == 'object') {
8360 this.after = Roo.factory(this.after);
8362 inputblock.cn.push({
8364 cls : 'roo-input-after input-group-' +
8365 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8369 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8370 inputblock.cls += ' has-feedback';
8371 inputblock.cn.push(feedback);
8375 if (align ==='left' && this.fieldLabel.length) {
8380 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8381 tooltip : 'This field is required'
8386 cls : 'control-label col-sm-' + this.labelWidth,
8387 html : this.fieldLabel
8391 cls : "col-sm-" + (12 - this.labelWidth),
8399 if(this.indicatorpos == 'right'){
8404 cls : 'control-label col-sm-' + this.labelWidth,
8405 html : this.fieldLabel
8410 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8411 tooltip : 'This field is required'
8414 cls : "col-sm-" + (12 - this.labelWidth),
8423 } else if ( this.fieldLabel.length) {
8428 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8429 tooltip : 'This field is required'
8433 //cls : 'input-group-addon',
8434 html : this.fieldLabel
8442 if(this.indicatorpos == 'right'){
8447 //cls : 'input-group-addon',
8448 html : this.fieldLabel
8453 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8454 tooltip : 'This field is required'
8474 if (this.parentType === 'Navbar' && this.parent().bar) {
8475 cfg.cls += ' navbar-form';
8478 if (this.parentType === 'NavGroup') {
8479 cfg.cls += ' navbar-form';
8487 * return the real input element.
8489 inputEl: function ()
8491 return this.el.select('input.form-control',true).first();
8494 tooltipEl : function()
8496 return this.inputEl();
8499 indicatorEl : function()
8501 var indicator = this.el.select('i.roo-required-indicator',true).first();
8511 setDisabled : function(v)
8513 var i = this.inputEl().dom;
8515 i.removeAttribute('disabled');
8519 i.setAttribute('disabled','true');
8521 initEvents : function()
8524 this.inputEl().on("keydown" , this.fireKey, this);
8525 this.inputEl().on("focus", this.onFocus, this);
8526 this.inputEl().on("blur", this.onBlur, this);
8528 this.inputEl().relayEvent('keyup', this);
8530 this.indicator = this.indicatorEl();
8533 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8534 this.indicator.hide();
8537 // reference to original value for reset
8538 this.originalValue = this.getValue();
8539 //Roo.form.TextField.superclass.initEvents.call(this);
8540 if(this.validationEvent == 'keyup'){
8541 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8542 this.inputEl().on('keyup', this.filterValidation, this);
8544 else if(this.validationEvent !== false){
8545 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8548 if(this.selectOnFocus){
8549 this.on("focus", this.preFocus, this);
8552 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8553 this.inputEl().on("keypress", this.filterKeys, this);
8555 this.inputEl().relayEvent('keypress', this);
8558 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8559 this.el.on("click", this.autoSize, this);
8562 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8563 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8566 if (typeof(this.before) == 'object') {
8567 this.before.render(this.el.select('.roo-input-before',true).first());
8569 if (typeof(this.after) == 'object') {
8570 this.after.render(this.el.select('.roo-input-after',true).first());
8575 filterValidation : function(e){
8576 if(!e.isNavKeyPress()){
8577 this.validationTask.delay(this.validationDelay);
8581 * Validates the field value
8582 * @return {Boolean} True if the value is valid, else false
8584 validate : function(){
8585 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8586 if(this.disabled || this.validateValue(this.getRawValue())){
8597 * Validates a value according to the field's validation rules and marks the field as invalid
8598 * if the validation fails
8599 * @param {Mixed} value The value to validate
8600 * @return {Boolean} True if the value is valid, else false
8602 validateValue : function(value){
8603 if(value.length < 1) { // if it's blank
8604 if(this.allowBlank){
8610 if(value.length < this.minLength){
8613 if(value.length > this.maxLength){
8617 var vt = Roo.form.VTypes;
8618 if(!vt[this.vtype](value, this)){
8622 if(typeof this.validator == "function"){
8623 var msg = this.validator(value);
8629 if(this.regex && !this.regex.test(value)){
8639 fireKey : function(e){
8640 //Roo.log('field ' + e.getKey());
8641 if(e.isNavKeyPress()){
8642 this.fireEvent("specialkey", this, e);
8645 focus : function (selectText){
8647 this.inputEl().focus();
8648 if(selectText === true){
8649 this.inputEl().dom.select();
8655 onFocus : function(){
8656 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8657 // this.el.addClass(this.focusClass);
8660 this.hasFocus = true;
8661 this.startValue = this.getValue();
8662 this.fireEvent("focus", this);
8666 beforeBlur : Roo.emptyFn,
8670 onBlur : function(){
8672 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8673 //this.el.removeClass(this.focusClass);
8675 this.hasFocus = false;
8676 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8679 var v = this.getValue();
8680 if(String(v) !== String(this.startValue)){
8681 this.fireEvent('change', this, v, this.startValue);
8683 this.fireEvent("blur", this);
8687 * Resets the current field value to the originally loaded value and clears any validation messages
8690 this.setValue(this.originalValue);
8694 * Returns the name of the field
8695 * @return {Mixed} name The name field
8697 getName: function(){
8701 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8702 * @return {Mixed} value The field value
8704 getValue : function(){
8706 var v = this.inputEl().getValue();
8711 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8712 * @return {Mixed} value The field value
8714 getRawValue : function(){
8715 var v = this.inputEl().getValue();
8721 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8722 * @param {Mixed} value The value to set
8724 setRawValue : function(v){
8725 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8728 selectText : function(start, end){
8729 var v = this.getRawValue();
8731 start = start === undefined ? 0 : start;
8732 end = end === undefined ? v.length : end;
8733 var d = this.inputEl().dom;
8734 if(d.setSelectionRange){
8735 d.setSelectionRange(start, end);
8736 }else if(d.createTextRange){
8737 var range = d.createTextRange();
8738 range.moveStart("character", start);
8739 range.moveEnd("character", v.length-end);
8746 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8747 * @param {Mixed} value The value to set
8749 setValue : function(v){
8752 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8758 processValue : function(value){
8759 if(this.stripCharsRe){
8760 var newValue = value.replace(this.stripCharsRe, '');
8761 if(newValue !== value){
8762 this.setRawValue(newValue);
8769 preFocus : function(){
8771 if(this.selectOnFocus){
8772 this.inputEl().dom.select();
8775 filterKeys : function(e){
8777 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8780 var c = e.getCharCode(), cc = String.fromCharCode(c);
8781 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8784 if(!this.maskRe.test(cc)){
8789 * Clear any invalid styles/messages for this field
8791 clearInvalid : function(){
8793 if(!this.el || this.preventMark){ // not rendered
8798 this.indicator.hide();
8801 this.el.removeClass(this.invalidClass);
8803 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8805 var feedback = this.el.select('.form-control-feedback', true).first();
8808 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8813 this.fireEvent('valid', this);
8817 * Mark this field as valid
8819 markValid : function()
8821 if(!this.el || this.preventMark){ // not rendered
8825 this.el.removeClass([this.invalidClass, this.validClass]);
8827 var feedback = this.el.select('.form-control-feedback', true).first();
8830 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8833 if(this.disabled || this.allowBlank){
8838 this.indicator.hide();
8841 this.el.addClass(this.validClass);
8843 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8845 var feedback = this.el.select('.form-control-feedback', true).first();
8848 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8849 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8854 this.fireEvent('valid', this);
8858 * Mark this field as invalid
8859 * @param {String} msg The validation message
8861 markInvalid : function(msg)
8863 if(!this.el || this.preventMark){ // not rendered
8867 this.el.removeClass([this.invalidClass, this.validClass]);
8869 var feedback = this.el.select('.form-control-feedback', true).first();
8872 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8875 if(this.disabled || this.allowBlank){
8880 this.indicator.show();
8883 this.el.addClass(this.invalidClass);
8885 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8887 var feedback = this.el.select('.form-control-feedback', true).first();
8890 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8892 if(this.getValue().length || this.forceFeedback){
8893 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8900 this.fireEvent('invalid', this, msg);
8903 SafariOnKeyDown : function(event)
8905 // this is a workaround for a password hang bug on chrome/ webkit.
8907 var isSelectAll = false;
8909 if(this.inputEl().dom.selectionEnd > 0){
8910 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8912 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8913 event.preventDefault();
8918 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8920 event.preventDefault();
8921 // this is very hacky as keydown always get's upper case.
8923 var cc = String.fromCharCode(event.getCharCode());
8924 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8928 adjustWidth : function(tag, w){
8929 tag = tag.toLowerCase();
8930 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8931 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8935 if(tag == 'textarea'){
8938 }else if(Roo.isOpera){
8942 if(tag == 'textarea'){
8961 * @class Roo.bootstrap.TextArea
8962 * @extends Roo.bootstrap.Input
8963 * Bootstrap TextArea class
8964 * @cfg {Number} cols Specifies the visible width of a text area
8965 * @cfg {Number} rows Specifies the visible number of lines in a text area
8966 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8967 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8968 * @cfg {string} html text
8971 * Create a new TextArea
8972 * @param {Object} config The config object
8975 Roo.bootstrap.TextArea = function(config){
8976 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8980 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8990 getAutoCreate : function(){
8992 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9003 value : this.value || '',
9004 html: this.html || '',
9005 cls : 'form-control',
9006 placeholder : this.placeholder || ''
9010 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9011 input.maxLength = this.maxLength;
9015 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9019 input.cols = this.cols;
9022 if (this.readOnly) {
9023 input.readonly = true;
9027 input.name = this.name;
9031 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9035 ['xs','sm','md','lg'].map(function(size){
9036 if (settings[size]) {
9037 cfg.cls += ' col-' + size + '-' + settings[size];
9041 var inputblock = input;
9043 if(this.hasFeedback && !this.allowBlank){
9047 cls: 'glyphicon form-control-feedback'
9051 cls : 'has-feedback',
9060 if (this.before || this.after) {
9063 cls : 'input-group',
9067 inputblock.cn.push({
9069 cls : 'input-group-addon',
9074 inputblock.cn.push(input);
9076 if(this.hasFeedback && !this.allowBlank){
9077 inputblock.cls += ' has-feedback';
9078 inputblock.cn.push(feedback);
9082 inputblock.cn.push({
9084 cls : 'input-group-addon',
9091 if (align ==='left' && this.fieldLabel.length) {
9092 // Roo.log("left and has label");
9098 cls : 'control-label col-sm-' + this.labelWidth,
9099 html : this.fieldLabel
9103 cls : "col-sm-" + (12 - this.labelWidth),
9110 } else if ( this.fieldLabel.length) {
9111 // Roo.log(" label");
9116 //cls : 'input-group-addon',
9117 html : this.fieldLabel
9127 // Roo.log(" no label && no align");
9137 if (this.disabled) {
9138 input.disabled=true;
9145 * return the real textarea element.
9147 inputEl: function ()
9149 return this.el.select('textarea.form-control',true).first();
9153 * Clear any invalid styles/messages for this field
9155 clearInvalid : function()
9158 if(!this.el || this.preventMark){ // not rendered
9162 var label = this.el.select('label', true).first();
9163 var icon = this.el.select('i.fa-star', true).first();
9169 this.el.removeClass(this.invalidClass);
9171 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9173 var feedback = this.el.select('.form-control-feedback', true).first();
9176 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9181 this.fireEvent('valid', this);
9185 * Mark this field as valid
9187 markValid : function()
9189 if(!this.el || this.preventMark){ // not rendered
9193 this.el.removeClass([this.invalidClass, this.validClass]);
9195 var feedback = this.el.select('.form-control-feedback', true).first();
9198 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9201 if(this.disabled || this.allowBlank){
9205 var label = this.el.select('label', true).first();
9206 var icon = this.el.select('i.fa-star', true).first();
9212 this.el.addClass(this.validClass);
9214 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9216 var feedback = this.el.select('.form-control-feedback', true).first();
9219 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9220 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9225 this.fireEvent('valid', this);
9229 * Mark this field as invalid
9230 * @param {String} msg The validation message
9232 markInvalid : function(msg)
9234 if(!this.el || this.preventMark){ // not rendered
9238 this.el.removeClass([this.invalidClass, this.validClass]);
9240 var feedback = this.el.select('.form-control-feedback', true).first();
9243 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9246 if(this.disabled || this.allowBlank){
9250 var label = this.el.select('label', true).first();
9251 var icon = this.el.select('i.fa-star', true).first();
9253 if(!this.getValue().length && label && !icon){
9254 this.el.createChild({
9256 cls : 'text-danger fa fa-lg fa-star',
9257 tooltip : 'This field is required',
9258 style : 'margin-right:5px;'
9262 this.el.addClass(this.invalidClass);
9264 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9266 var feedback = this.el.select('.form-control-feedback', true).first();
9269 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9271 if(this.getValue().length || this.forceFeedback){
9272 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9279 this.fireEvent('invalid', this, msg);
9287 * trigger field - base class for combo..
9292 * @class Roo.bootstrap.TriggerField
9293 * @extends Roo.bootstrap.Input
9294 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9295 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9296 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9297 * for which you can provide a custom implementation. For example:
9299 var trigger = new Roo.bootstrap.TriggerField();
9300 trigger.onTriggerClick = myTriggerFn;
9301 trigger.applyTo('my-field');
9304 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9305 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9306 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9307 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9308 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9311 * Create a new TriggerField.
9312 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9313 * to the base TextField)
9315 Roo.bootstrap.TriggerField = function(config){
9316 this.mimicing = false;
9317 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9320 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9322 * @cfg {String} triggerClass A CSS class to apply to the trigger
9325 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9330 * @cfg {Boolean} removable (true|false) special filter default false
9334 /** @cfg {Boolean} grow @hide */
9335 /** @cfg {Number} growMin @hide */
9336 /** @cfg {Number} growMax @hide */
9342 autoSize: Roo.emptyFn,
9349 actionMode : 'wrap',
9354 getAutoCreate : function(){
9356 var align = this.labelAlign || this.parentLabelAlign();
9361 cls: 'form-group' //input-group
9368 type : this.inputType,
9369 cls : 'form-control',
9370 autocomplete: 'new-password',
9371 placeholder : this.placeholder || ''
9375 input.name = this.name;
9378 input.cls += ' input-' + this.size;
9381 if (this.disabled) {
9382 input.disabled=true;
9385 var inputblock = input;
9387 if(this.hasFeedback && !this.allowBlank){
9391 cls: 'glyphicon form-control-feedback'
9394 if(this.removable && !this.editable && !this.tickable){
9396 cls : 'has-feedback',
9402 cls : 'roo-combo-removable-btn close'
9409 cls : 'has-feedback',
9418 if(this.removable && !this.editable && !this.tickable){
9420 cls : 'roo-removable',
9426 cls : 'roo-combo-removable-btn close'
9433 if (this.before || this.after) {
9436 cls : 'input-group',
9440 inputblock.cn.push({
9442 cls : 'input-group-addon',
9447 inputblock.cn.push(input);
9449 if(this.hasFeedback && !this.allowBlank){
9450 inputblock.cls += ' has-feedback';
9451 inputblock.cn.push(feedback);
9455 inputblock.cn.push({
9457 cls : 'input-group-addon',
9470 cls: 'form-hidden-field'
9484 cls: 'form-hidden-field'
9488 cls: 'roo-select2-choices',
9492 cls: 'roo-select2-search-field',
9505 cls: 'roo-select2-container input-group',
9510 // cls: 'typeahead typeahead-long dropdown-menu',
9511 // style: 'display:none'
9516 if(!this.multiple && this.showToggleBtn){
9522 if (this.caret != false) {
9525 cls: 'fa fa-' + this.caret
9532 cls : 'input-group-addon btn dropdown-toggle',
9537 cls: 'combobox-clear',
9551 combobox.cls += ' roo-select2-container-multi';
9554 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9556 // Roo.log("left and has label");
9560 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9561 tooltip : 'This field is required'
9566 cls : 'control-label col-sm-' + this.labelWidth,
9567 html : this.fieldLabel
9571 cls : "col-sm-" + (12 - this.labelWidth),
9579 if(this.indicatorpos == 'right'){
9584 cls : 'control-label col-sm-' + this.labelWidth,
9585 html : this.fieldLabel
9590 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9591 tooltip : 'This field is required'
9594 cls : "col-sm-" + (12 - this.labelWidth),
9603 } else if ( this.fieldLabel.length) {
9604 // Roo.log(" label");
9608 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9609 tooltip : 'This field is required'
9613 //cls : 'input-group-addon',
9614 html : this.fieldLabel
9622 if(this.indicatorpos == 'right'){
9627 //cls : 'input-group-addon',
9628 html : this.fieldLabel
9633 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9634 tooltip : 'This field is required'
9645 // Roo.log(" no label && no align");
9652 ['xs','sm','md','lg'].map(function(size){
9653 if (settings[size]) {
9654 cfg.cls += ' col-' + size + '-' + settings[size];
9665 onResize : function(w, h){
9666 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9667 // if(typeof w == 'number'){
9668 // var x = w - this.trigger.getWidth();
9669 // this.inputEl().setWidth(this.adjustWidth('input', x));
9670 // this.trigger.setStyle('left', x+'px');
9675 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9678 getResizeEl : function(){
9679 return this.inputEl();
9683 getPositionEl : function(){
9684 return this.inputEl();
9688 alignErrorIcon : function(){
9689 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9693 initEvents : function(){
9697 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9698 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9699 if(!this.multiple && this.showToggleBtn){
9700 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9701 if(this.hideTrigger){
9702 this.trigger.setDisplayed(false);
9704 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9708 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9711 if(this.removable && !this.editable && !this.tickable){
9712 var close = this.closeTriggerEl();
9715 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9716 close.on('click', this.removeBtnClick, this, close);
9720 //this.trigger.addClassOnOver('x-form-trigger-over');
9721 //this.trigger.addClassOnClick('x-form-trigger-click');
9724 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9728 closeTriggerEl : function()
9730 var close = this.el.select('.roo-combo-removable-btn', true).first();
9731 return close ? close : false;
9734 removeBtnClick : function(e, h, el)
9738 if(this.fireEvent("remove", this) !== false){
9740 this.fireEvent("afterremove", this)
9744 createList : function()
9746 this.list = Roo.get(document.body).createChild({
9748 cls: 'typeahead typeahead-long dropdown-menu',
9749 style: 'display:none'
9752 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9757 initTrigger : function(){
9762 onDestroy : function(){
9764 this.trigger.removeAllListeners();
9765 // this.trigger.remove();
9768 // this.wrap.remove();
9770 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9774 onFocus : function(){
9775 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9778 this.wrap.addClass('x-trigger-wrap-focus');
9779 this.mimicing = true;
9780 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9781 if(this.monitorTab){
9782 this.el.on("keydown", this.checkTab, this);
9789 checkTab : function(e){
9790 if(e.getKey() == e.TAB){
9796 onBlur : function(){
9801 mimicBlur : function(e, t){
9803 if(!this.wrap.contains(t) && this.validateBlur()){
9810 triggerBlur : function(){
9811 this.mimicing = false;
9812 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9813 if(this.monitorTab){
9814 this.el.un("keydown", this.checkTab, this);
9816 //this.wrap.removeClass('x-trigger-wrap-focus');
9817 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9821 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9822 validateBlur : function(e, t){
9827 onDisable : function(){
9828 this.inputEl().dom.disabled = true;
9829 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9831 // this.wrap.addClass('x-item-disabled');
9836 onEnable : function(){
9837 this.inputEl().dom.disabled = false;
9838 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9840 // this.el.removeClass('x-item-disabled');
9845 onShow : function(){
9846 var ae = this.getActionEl();
9849 ae.dom.style.display = '';
9850 ae.dom.style.visibility = 'visible';
9856 onHide : function(){
9857 var ae = this.getActionEl();
9858 ae.dom.style.display = 'none';
9862 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9863 * by an implementing function.
9865 * @param {EventObject} e
9867 onTriggerClick : Roo.emptyFn
9871 * Ext JS Library 1.1.1
9872 * Copyright(c) 2006-2007, Ext JS, LLC.
9874 * Originally Released Under LGPL - original licence link has changed is not relivant.
9877 * <script type="text/javascript">
9882 * @class Roo.data.SortTypes
9884 * Defines the default sorting (casting?) comparison functions used when sorting data.
9886 Roo.data.SortTypes = {
9888 * Default sort that does nothing
9889 * @param {Mixed} s The value being converted
9890 * @return {Mixed} The comparison value
9897 * The regular expression used to strip tags
9901 stripTagsRE : /<\/?[^>]+>/gi,
9904 * Strips all HTML tags to sort on text only
9905 * @param {Mixed} s The value being converted
9906 * @return {String} The comparison value
9908 asText : function(s){
9909 return String(s).replace(this.stripTagsRE, "");
9913 * Strips all HTML tags to sort on text only - Case insensitive
9914 * @param {Mixed} s The value being converted
9915 * @return {String} The comparison value
9917 asUCText : function(s){
9918 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9922 * Case insensitive string
9923 * @param {Mixed} s The value being converted
9924 * @return {String} The comparison value
9926 asUCString : function(s) {
9927 return String(s).toUpperCase();
9932 * @param {Mixed} s The value being converted
9933 * @return {Number} The comparison value
9935 asDate : function(s) {
9939 if(s instanceof Date){
9942 return Date.parse(String(s));
9947 * @param {Mixed} s The value being converted
9948 * @return {Float} The comparison value
9950 asFloat : function(s) {
9951 var val = parseFloat(String(s).replace(/,/g, ""));
9960 * @param {Mixed} s The value being converted
9961 * @return {Number} The comparison value
9963 asInt : function(s) {
9964 var val = parseInt(String(s).replace(/,/g, ""));
9972 * Ext JS Library 1.1.1
9973 * Copyright(c) 2006-2007, Ext JS, LLC.
9975 * Originally Released Under LGPL - original licence link has changed is not relivant.
9978 * <script type="text/javascript">
9982 * @class Roo.data.Record
9983 * Instances of this class encapsulate both record <em>definition</em> information, and record
9984 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9985 * to access Records cached in an {@link Roo.data.Store} object.<br>
9987 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9988 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9991 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9993 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9994 * {@link #create}. The parameters are the same.
9995 * @param {Array} data An associative Array of data values keyed by the field name.
9996 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9997 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9998 * not specified an integer id is generated.
10000 Roo.data.Record = function(data, id){
10001 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10006 * Generate a constructor for a specific record layout.
10007 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10008 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10009 * Each field definition object may contain the following properties: <ul>
10010 * <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,
10011 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10012 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10013 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10014 * is being used, then this is a string containing the javascript expression to reference the data relative to
10015 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10016 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10017 * this may be omitted.</p></li>
10018 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10019 * <ul><li>auto (Default, implies no conversion)</li>
10024 * <li>date</li></ul></p></li>
10025 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10026 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10027 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10028 * by the Reader into an object that will be stored in the Record. It is passed the
10029 * following parameters:<ul>
10030 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10032 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10034 * <br>usage:<br><pre><code>
10035 var TopicRecord = Roo.data.Record.create(
10036 {name: 'title', mapping: 'topic_title'},
10037 {name: 'author', mapping: 'username'},
10038 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10039 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10040 {name: 'lastPoster', mapping: 'user2'},
10041 {name: 'excerpt', mapping: 'post_text'}
10044 var myNewRecord = new TopicRecord({
10045 title: 'Do my job please',
10048 lastPost: new Date(),
10049 lastPoster: 'Animal',
10050 excerpt: 'No way dude!'
10052 myStore.add(myNewRecord);
10057 Roo.data.Record.create = function(o){
10058 var f = function(){
10059 f.superclass.constructor.apply(this, arguments);
10061 Roo.extend(f, Roo.data.Record);
10062 var p = f.prototype;
10063 p.fields = new Roo.util.MixedCollection(false, function(field){
10066 for(var i = 0, len = o.length; i < len; i++){
10067 p.fields.add(new Roo.data.Field(o[i]));
10069 f.getField = function(name){
10070 return p.fields.get(name);
10075 Roo.data.Record.AUTO_ID = 1000;
10076 Roo.data.Record.EDIT = 'edit';
10077 Roo.data.Record.REJECT = 'reject';
10078 Roo.data.Record.COMMIT = 'commit';
10080 Roo.data.Record.prototype = {
10082 * Readonly flag - true if this record has been modified.
10091 join : function(store){
10092 this.store = store;
10096 * Set the named field to the specified value.
10097 * @param {String} name The name of the field to set.
10098 * @param {Object} value The value to set the field to.
10100 set : function(name, value){
10101 if(this.data[name] == value){
10105 if(!this.modified){
10106 this.modified = {};
10108 if(typeof this.modified[name] == 'undefined'){
10109 this.modified[name] = this.data[name];
10111 this.data[name] = value;
10112 if(!this.editing && this.store){
10113 this.store.afterEdit(this);
10118 * Get the value of the named field.
10119 * @param {String} name The name of the field to get the value of.
10120 * @return {Object} The value of the field.
10122 get : function(name){
10123 return this.data[name];
10127 beginEdit : function(){
10128 this.editing = true;
10129 this.modified = {};
10133 cancelEdit : function(){
10134 this.editing = false;
10135 delete this.modified;
10139 endEdit : function(){
10140 this.editing = false;
10141 if(this.dirty && this.store){
10142 this.store.afterEdit(this);
10147 * Usually called by the {@link Roo.data.Store} which owns the Record.
10148 * Rejects all changes made to the Record since either creation, or the last commit operation.
10149 * Modified fields are reverted to their original values.
10151 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10152 * of reject operations.
10154 reject : function(){
10155 var m = this.modified;
10157 if(typeof m[n] != "function"){
10158 this.data[n] = m[n];
10161 this.dirty = false;
10162 delete this.modified;
10163 this.editing = false;
10165 this.store.afterReject(this);
10170 * Usually called by the {@link Roo.data.Store} which owns the Record.
10171 * Commits all changes made to the Record since either creation, or the last commit operation.
10173 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10174 * of commit operations.
10176 commit : function(){
10177 this.dirty = false;
10178 delete this.modified;
10179 this.editing = false;
10181 this.store.afterCommit(this);
10186 hasError : function(){
10187 return this.error != null;
10191 clearError : function(){
10196 * Creates a copy of this record.
10197 * @param {String} id (optional) A new record id if you don't want to use this record's id
10200 copy : function(newId) {
10201 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10205 * Ext JS Library 1.1.1
10206 * Copyright(c) 2006-2007, Ext JS, LLC.
10208 * Originally Released Under LGPL - original licence link has changed is not relivant.
10211 * <script type="text/javascript">
10217 * @class Roo.data.Store
10218 * @extends Roo.util.Observable
10219 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10220 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10222 * 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
10223 * has no knowledge of the format of the data returned by the Proxy.<br>
10225 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10226 * instances from the data object. These records are cached and made available through accessor functions.
10228 * Creates a new Store.
10229 * @param {Object} config A config object containing the objects needed for the Store to access data,
10230 * and read the data into Records.
10232 Roo.data.Store = function(config){
10233 this.data = new Roo.util.MixedCollection(false);
10234 this.data.getKey = function(o){
10237 this.baseParams = {};
10239 this.paramNames = {
10244 "multisort" : "_multisort"
10247 if(config && config.data){
10248 this.inlineData = config.data;
10249 delete config.data;
10252 Roo.apply(this, config);
10254 if(this.reader){ // reader passed
10255 this.reader = Roo.factory(this.reader, Roo.data);
10256 this.reader.xmodule = this.xmodule || false;
10257 if(!this.recordType){
10258 this.recordType = this.reader.recordType;
10260 if(this.reader.onMetaChange){
10261 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10265 if(this.recordType){
10266 this.fields = this.recordType.prototype.fields;
10268 this.modified = [];
10272 * @event datachanged
10273 * Fires when the data cache has changed, and a widget which is using this Store
10274 * as a Record cache should refresh its view.
10275 * @param {Store} this
10277 datachanged : true,
10279 * @event metachange
10280 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10281 * @param {Store} this
10282 * @param {Object} meta The JSON metadata
10287 * Fires when Records have been added to the Store
10288 * @param {Store} this
10289 * @param {Roo.data.Record[]} records The array of Records added
10290 * @param {Number} index The index at which the record(s) were added
10295 * Fires when a Record has been removed from the Store
10296 * @param {Store} this
10297 * @param {Roo.data.Record} record The Record that was removed
10298 * @param {Number} index The index at which the record was removed
10303 * Fires when a Record has been updated
10304 * @param {Store} this
10305 * @param {Roo.data.Record} record The Record that was updated
10306 * @param {String} operation The update operation being performed. Value may be one of:
10308 Roo.data.Record.EDIT
10309 Roo.data.Record.REJECT
10310 Roo.data.Record.COMMIT
10316 * Fires when the data cache has been cleared.
10317 * @param {Store} this
10321 * @event beforeload
10322 * Fires before a request is made for a new data object. If the beforeload handler returns false
10323 * the load action will be canceled.
10324 * @param {Store} this
10325 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10329 * @event beforeloadadd
10330 * Fires after a new set of Records has been loaded.
10331 * @param {Store} this
10332 * @param {Roo.data.Record[]} records The Records that were loaded
10333 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10335 beforeloadadd : true,
10338 * Fires after a new set of Records has been loaded, before they are added to the store.
10339 * @param {Store} this
10340 * @param {Roo.data.Record[]} records The Records that were loaded
10341 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10342 * @params {Object} return from reader
10346 * @event loadexception
10347 * Fires if an exception occurs in the Proxy during loading.
10348 * Called with the signature of the Proxy's "loadexception" event.
10349 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10352 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10353 * @param {Object} load options
10354 * @param {Object} jsonData from your request (normally this contains the Exception)
10356 loadexception : true
10360 this.proxy = Roo.factory(this.proxy, Roo.data);
10361 this.proxy.xmodule = this.xmodule || false;
10362 this.relayEvents(this.proxy, ["loadexception"]);
10364 this.sortToggle = {};
10365 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10367 Roo.data.Store.superclass.constructor.call(this);
10369 if(this.inlineData){
10370 this.loadData(this.inlineData);
10371 delete this.inlineData;
10375 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10377 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10378 * without a remote query - used by combo/forms at present.
10382 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10385 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10388 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10389 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10392 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10393 * on any HTTP request
10396 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10399 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10403 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10404 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10406 remoteSort : false,
10409 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10410 * loaded or when a record is removed. (defaults to false).
10412 pruneModifiedRecords : false,
10415 lastOptions : null,
10418 * Add Records to the Store and fires the add event.
10419 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10421 add : function(records){
10422 records = [].concat(records);
10423 for(var i = 0, len = records.length; i < len; i++){
10424 records[i].join(this);
10426 var index = this.data.length;
10427 this.data.addAll(records);
10428 this.fireEvent("add", this, records, index);
10432 * Remove a Record from the Store and fires the remove event.
10433 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10435 remove : function(record){
10436 var index = this.data.indexOf(record);
10437 this.data.removeAt(index);
10438 if(this.pruneModifiedRecords){
10439 this.modified.remove(record);
10441 this.fireEvent("remove", this, record, index);
10445 * Remove all Records from the Store and fires the clear event.
10447 removeAll : function(){
10449 if(this.pruneModifiedRecords){
10450 this.modified = [];
10452 this.fireEvent("clear", this);
10456 * Inserts Records to the Store at the given index and fires the add event.
10457 * @param {Number} index The start index at which to insert the passed Records.
10458 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10460 insert : function(index, records){
10461 records = [].concat(records);
10462 for(var i = 0, len = records.length; i < len; i++){
10463 this.data.insert(index, records[i]);
10464 records[i].join(this);
10466 this.fireEvent("add", this, records, index);
10470 * Get the index within the cache of the passed Record.
10471 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10472 * @return {Number} The index of the passed Record. Returns -1 if not found.
10474 indexOf : function(record){
10475 return this.data.indexOf(record);
10479 * Get the index within the cache of the Record with the passed id.
10480 * @param {String} id The id of the Record to find.
10481 * @return {Number} The index of the Record. Returns -1 if not found.
10483 indexOfId : function(id){
10484 return this.data.indexOfKey(id);
10488 * Get the Record with the specified id.
10489 * @param {String} id The id of the Record to find.
10490 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10492 getById : function(id){
10493 return this.data.key(id);
10497 * Get the Record at the specified index.
10498 * @param {Number} index The index of the Record to find.
10499 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10501 getAt : function(index){
10502 return this.data.itemAt(index);
10506 * Returns a range of Records between specified indices.
10507 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10508 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10509 * @return {Roo.data.Record[]} An array of Records
10511 getRange : function(start, end){
10512 return this.data.getRange(start, end);
10516 storeOptions : function(o){
10517 o = Roo.apply({}, o);
10520 this.lastOptions = o;
10524 * Loads the Record cache from the configured Proxy using the configured Reader.
10526 * If using remote paging, then the first load call must specify the <em>start</em>
10527 * and <em>limit</em> properties in the options.params property to establish the initial
10528 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10530 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10531 * and this call will return before the new data has been loaded. Perform any post-processing
10532 * in a callback function, or in a "load" event handler.</strong>
10534 * @param {Object} options An object containing properties which control loading options:<ul>
10535 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10536 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10537 * passed the following arguments:<ul>
10538 * <li>r : Roo.data.Record[]</li>
10539 * <li>options: Options object from the load call</li>
10540 * <li>success: Boolean success indicator</li></ul></li>
10541 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10542 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10545 load : function(options){
10546 options = options || {};
10547 if(this.fireEvent("beforeload", this, options) !== false){
10548 this.storeOptions(options);
10549 var p = Roo.apply(options.params || {}, this.baseParams);
10550 // if meta was not loaded from remote source.. try requesting it.
10551 if (!this.reader.metaFromRemote) {
10552 p._requestMeta = 1;
10554 if(this.sortInfo && this.remoteSort){
10555 var pn = this.paramNames;
10556 p[pn["sort"]] = this.sortInfo.field;
10557 p[pn["dir"]] = this.sortInfo.direction;
10559 if (this.multiSort) {
10560 var pn = this.paramNames;
10561 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10564 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10569 * Reloads the Record cache from the configured Proxy using the configured Reader and
10570 * the options from the last load operation performed.
10571 * @param {Object} options (optional) An object containing properties which may override the options
10572 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10573 * the most recently used options are reused).
10575 reload : function(options){
10576 this.load(Roo.applyIf(options||{}, this.lastOptions));
10580 // Called as a callback by the Reader during a load operation.
10581 loadRecords : function(o, options, success){
10582 if(!o || success === false){
10583 if(success !== false){
10584 this.fireEvent("load", this, [], options, o);
10586 if(options.callback){
10587 options.callback.call(options.scope || this, [], options, false);
10591 // if data returned failure - throw an exception.
10592 if (o.success === false) {
10593 // show a message if no listener is registered.
10594 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10595 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10597 // loadmask wil be hooked into this..
10598 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10601 var r = o.records, t = o.totalRecords || r.length;
10603 this.fireEvent("beforeloadadd", this, r, options, o);
10605 if(!options || options.add !== true){
10606 if(this.pruneModifiedRecords){
10607 this.modified = [];
10609 for(var i = 0, len = r.length; i < len; i++){
10613 this.data = this.snapshot;
10614 delete this.snapshot;
10617 this.data.addAll(r);
10618 this.totalLength = t;
10620 this.fireEvent("datachanged", this);
10622 this.totalLength = Math.max(t, this.data.length+r.length);
10625 this.fireEvent("load", this, r, options, o);
10626 if(options.callback){
10627 options.callback.call(options.scope || this, r, options, true);
10633 * Loads data from a passed data block. A Reader which understands the format of the data
10634 * must have been configured in the constructor.
10635 * @param {Object} data The data block from which to read the Records. The format of the data expected
10636 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10637 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10639 loadData : function(o, append){
10640 var r = this.reader.readRecords(o);
10641 this.loadRecords(r, {add: append}, true);
10645 * Gets the number of cached records.
10647 * <em>If using paging, this may not be the total size of the dataset. If the data object
10648 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10649 * the data set size</em>
10651 getCount : function(){
10652 return this.data.length || 0;
10656 * Gets the total number of records in the dataset as returned by the server.
10658 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10659 * the dataset size</em>
10661 getTotalCount : function(){
10662 return this.totalLength || 0;
10666 * Returns the sort state of the Store as an object with two properties:
10668 field {String} The name of the field by which the Records are sorted
10669 direction {String} The sort order, "ASC" or "DESC"
10672 getSortState : function(){
10673 return this.sortInfo;
10677 applySort : function(){
10678 if(this.sortInfo && !this.remoteSort){
10679 var s = this.sortInfo, f = s.field;
10680 var st = this.fields.get(f).sortType;
10681 var fn = function(r1, r2){
10682 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10683 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10685 this.data.sort(s.direction, fn);
10686 if(this.snapshot && this.snapshot != this.data){
10687 this.snapshot.sort(s.direction, fn);
10693 * Sets the default sort column and order to be used by the next load operation.
10694 * @param {String} fieldName The name of the field to sort by.
10695 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10697 setDefaultSort : function(field, dir){
10698 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10702 * Sort the Records.
10703 * If remote sorting is used, the sort is performed on the server, and the cache is
10704 * reloaded. If local sorting is used, the cache is sorted internally.
10705 * @param {String} fieldName The name of the field to sort by.
10706 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10708 sort : function(fieldName, dir){
10709 var f = this.fields.get(fieldName);
10711 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10713 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10714 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10719 this.sortToggle[f.name] = dir;
10720 this.sortInfo = {field: f.name, direction: dir};
10721 if(!this.remoteSort){
10723 this.fireEvent("datachanged", this);
10725 this.load(this.lastOptions);
10730 * Calls the specified function for each of the Records in the cache.
10731 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10732 * Returning <em>false</em> aborts and exits the iteration.
10733 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10735 each : function(fn, scope){
10736 this.data.each(fn, scope);
10740 * Gets all records modified since the last commit. Modified records are persisted across load operations
10741 * (e.g., during paging).
10742 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10744 getModifiedRecords : function(){
10745 return this.modified;
10749 createFilterFn : function(property, value, anyMatch){
10750 if(!value.exec){ // not a regex
10751 value = String(value);
10752 if(value.length == 0){
10755 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10757 return function(r){
10758 return value.test(r.data[property]);
10763 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10764 * @param {String} property A field on your records
10765 * @param {Number} start The record index to start at (defaults to 0)
10766 * @param {Number} end The last record index to include (defaults to length - 1)
10767 * @return {Number} The sum
10769 sum : function(property, start, end){
10770 var rs = this.data.items, v = 0;
10771 start = start || 0;
10772 end = (end || end === 0) ? end : rs.length-1;
10774 for(var i = start; i <= end; i++){
10775 v += (rs[i].data[property] || 0);
10781 * Filter the records by a specified property.
10782 * @param {String} field A field on your records
10783 * @param {String/RegExp} value Either a string that the field
10784 * should start with or a RegExp to test against the field
10785 * @param {Boolean} anyMatch True to match any part not just the beginning
10787 filter : function(property, value, anyMatch){
10788 var fn = this.createFilterFn(property, value, anyMatch);
10789 return fn ? this.filterBy(fn) : this.clearFilter();
10793 * Filter by a function. The specified function will be called with each
10794 * record in this data source. If the function returns true the record is included,
10795 * otherwise it is filtered.
10796 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10797 * @param {Object} scope (optional) The scope of the function (defaults to this)
10799 filterBy : function(fn, scope){
10800 this.snapshot = this.snapshot || this.data;
10801 this.data = this.queryBy(fn, scope||this);
10802 this.fireEvent("datachanged", this);
10806 * Query the records by a specified property.
10807 * @param {String} field A field on your records
10808 * @param {String/RegExp} value Either a string that the field
10809 * should start with or a RegExp to test against the field
10810 * @param {Boolean} anyMatch True to match any part not just the beginning
10811 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10813 query : function(property, value, anyMatch){
10814 var fn = this.createFilterFn(property, value, anyMatch);
10815 return fn ? this.queryBy(fn) : this.data.clone();
10819 * Query by a function. The specified function will be called with each
10820 * record in this data source. If the function returns true the record is included
10822 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10823 * @param {Object} scope (optional) The scope of the function (defaults to this)
10824 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10826 queryBy : function(fn, scope){
10827 var data = this.snapshot || this.data;
10828 return data.filterBy(fn, scope||this);
10832 * Collects unique values for a particular dataIndex from this store.
10833 * @param {String} dataIndex The property to collect
10834 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10835 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10836 * @return {Array} An array of the unique values
10838 collect : function(dataIndex, allowNull, bypassFilter){
10839 var d = (bypassFilter === true && this.snapshot) ?
10840 this.snapshot.items : this.data.items;
10841 var v, sv, r = [], l = {};
10842 for(var i = 0, len = d.length; i < len; i++){
10843 v = d[i].data[dataIndex];
10845 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10854 * Revert to a view of the Record cache with no filtering applied.
10855 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10857 clearFilter : function(suppressEvent){
10858 if(this.snapshot && this.snapshot != this.data){
10859 this.data = this.snapshot;
10860 delete this.snapshot;
10861 if(suppressEvent !== true){
10862 this.fireEvent("datachanged", this);
10868 afterEdit : function(record){
10869 if(this.modified.indexOf(record) == -1){
10870 this.modified.push(record);
10872 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10876 afterReject : function(record){
10877 this.modified.remove(record);
10878 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10882 afterCommit : function(record){
10883 this.modified.remove(record);
10884 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10888 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10889 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10891 commitChanges : function(){
10892 var m = this.modified.slice(0);
10893 this.modified = [];
10894 for(var i = 0, len = m.length; i < len; i++){
10900 * Cancel outstanding changes on all changed records.
10902 rejectChanges : function(){
10903 var m = this.modified.slice(0);
10904 this.modified = [];
10905 for(var i = 0, len = m.length; i < len; i++){
10910 onMetaChange : function(meta, rtype, o){
10911 this.recordType = rtype;
10912 this.fields = rtype.prototype.fields;
10913 delete this.snapshot;
10914 this.sortInfo = meta.sortInfo || this.sortInfo;
10915 this.modified = [];
10916 this.fireEvent('metachange', this, this.reader.meta);
10919 moveIndex : function(data, type)
10921 var index = this.indexOf(data);
10923 var newIndex = index + type;
10927 this.insert(newIndex, data);
10932 * Ext JS Library 1.1.1
10933 * Copyright(c) 2006-2007, Ext JS, LLC.
10935 * Originally Released Under LGPL - original licence link has changed is not relivant.
10938 * <script type="text/javascript">
10942 * @class Roo.data.SimpleStore
10943 * @extends Roo.data.Store
10944 * Small helper class to make creating Stores from Array data easier.
10945 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10946 * @cfg {Array} fields An array of field definition objects, or field name strings.
10947 * @cfg {Array} data The multi-dimensional array of data
10949 * @param {Object} config
10951 Roo.data.SimpleStore = function(config){
10952 Roo.data.SimpleStore.superclass.constructor.call(this, {
10954 reader: new Roo.data.ArrayReader({
10957 Roo.data.Record.create(config.fields)
10959 proxy : new Roo.data.MemoryProxy(config.data)
10963 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10965 * Ext JS Library 1.1.1
10966 * Copyright(c) 2006-2007, Ext JS, LLC.
10968 * Originally Released Under LGPL - original licence link has changed is not relivant.
10971 * <script type="text/javascript">
10976 * @extends Roo.data.Store
10977 * @class Roo.data.JsonStore
10978 * Small helper class to make creating Stores for JSON data easier. <br/>
10980 var store = new Roo.data.JsonStore({
10981 url: 'get-images.php',
10983 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10986 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10987 * JsonReader and HttpProxy (unless inline data is provided).</b>
10988 * @cfg {Array} fields An array of field definition objects, or field name strings.
10990 * @param {Object} config
10992 Roo.data.JsonStore = function(c){
10993 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10994 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10995 reader: new Roo.data.JsonReader(c, c.fields)
10998 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11000 * Ext JS Library 1.1.1
11001 * Copyright(c) 2006-2007, Ext JS, LLC.
11003 * Originally Released Under LGPL - original licence link has changed is not relivant.
11006 * <script type="text/javascript">
11010 Roo.data.Field = function(config){
11011 if(typeof config == "string"){
11012 config = {name: config};
11014 Roo.apply(this, config);
11017 this.type = "auto";
11020 var st = Roo.data.SortTypes;
11021 // named sortTypes are supported, here we look them up
11022 if(typeof this.sortType == "string"){
11023 this.sortType = st[this.sortType];
11026 // set default sortType for strings and dates
11027 if(!this.sortType){
11030 this.sortType = st.asUCString;
11033 this.sortType = st.asDate;
11036 this.sortType = st.none;
11041 var stripRe = /[\$,%]/g;
11043 // prebuilt conversion function for this field, instead of
11044 // switching every time we're reading a value
11046 var cv, dateFormat = this.dateFormat;
11051 cv = function(v){ return v; };
11054 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11058 return v !== undefined && v !== null && v !== '' ?
11059 parseInt(String(v).replace(stripRe, ""), 10) : '';
11064 return v !== undefined && v !== null && v !== '' ?
11065 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11070 cv = function(v){ return v === true || v === "true" || v == 1; };
11077 if(v instanceof Date){
11081 if(dateFormat == "timestamp"){
11082 return new Date(v*1000);
11084 return Date.parseDate(v, dateFormat);
11086 var parsed = Date.parse(v);
11087 return parsed ? new Date(parsed) : null;
11096 Roo.data.Field.prototype = {
11104 * Ext JS Library 1.1.1
11105 * Copyright(c) 2006-2007, Ext JS, LLC.
11107 * Originally Released Under LGPL - original licence link has changed is not relivant.
11110 * <script type="text/javascript">
11113 // Base class for reading structured data from a data source. This class is intended to be
11114 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11117 * @class Roo.data.DataReader
11118 * Base class for reading structured data from a data source. This class is intended to be
11119 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11122 Roo.data.DataReader = function(meta, recordType){
11126 this.recordType = recordType instanceof Array ?
11127 Roo.data.Record.create(recordType) : recordType;
11130 Roo.data.DataReader.prototype = {
11132 * Create an empty record
11133 * @param {Object} data (optional) - overlay some values
11134 * @return {Roo.data.Record} record created.
11136 newRow : function(d) {
11138 this.recordType.prototype.fields.each(function(c) {
11140 case 'int' : da[c.name] = 0; break;
11141 case 'date' : da[c.name] = new Date(); break;
11142 case 'float' : da[c.name] = 0.0; break;
11143 case 'boolean' : da[c.name] = false; break;
11144 default : da[c.name] = ""; break;
11148 return new this.recordType(Roo.apply(da, d));
11153 * Ext JS Library 1.1.1
11154 * Copyright(c) 2006-2007, Ext JS, LLC.
11156 * Originally Released Under LGPL - original licence link has changed is not relivant.
11159 * <script type="text/javascript">
11163 * @class Roo.data.DataProxy
11164 * @extends Roo.data.Observable
11165 * This class is an abstract base class for implementations which provide retrieval of
11166 * unformatted data objects.<br>
11168 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11169 * (of the appropriate type which knows how to parse the data object) to provide a block of
11170 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11172 * Custom implementations must implement the load method as described in
11173 * {@link Roo.data.HttpProxy#load}.
11175 Roo.data.DataProxy = function(){
11178 * @event beforeload
11179 * Fires before a network request is made to retrieve a data object.
11180 * @param {Object} This DataProxy object.
11181 * @param {Object} params The params parameter to the load function.
11186 * Fires before the load method's callback is called.
11187 * @param {Object} This DataProxy object.
11188 * @param {Object} o The data object.
11189 * @param {Object} arg The callback argument object passed to the load function.
11193 * @event loadexception
11194 * Fires if an Exception occurs during data retrieval.
11195 * @param {Object} This DataProxy object.
11196 * @param {Object} o The data object.
11197 * @param {Object} arg The callback argument object passed to the load function.
11198 * @param {Object} e The Exception.
11200 loadexception : true
11202 Roo.data.DataProxy.superclass.constructor.call(this);
11205 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11208 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11212 * Ext JS Library 1.1.1
11213 * Copyright(c) 2006-2007, Ext JS, LLC.
11215 * Originally Released Under LGPL - original licence link has changed is not relivant.
11218 * <script type="text/javascript">
11221 * @class Roo.data.MemoryProxy
11222 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11223 * to the Reader when its load method is called.
11225 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11227 Roo.data.MemoryProxy = function(data){
11231 Roo.data.MemoryProxy.superclass.constructor.call(this);
11235 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11238 * Load data from the requested source (in this case an in-memory
11239 * data object passed to the constructor), read the data object into
11240 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11241 * process that block using the passed callback.
11242 * @param {Object} params This parameter is not used by the MemoryProxy class.
11243 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11244 * object into a block of Roo.data.Records.
11245 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11246 * The function must be passed <ul>
11247 * <li>The Record block object</li>
11248 * <li>The "arg" argument from the load function</li>
11249 * <li>A boolean success indicator</li>
11251 * @param {Object} scope The scope in which to call the callback
11252 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11254 load : function(params, reader, callback, scope, arg){
11255 params = params || {};
11258 result = reader.readRecords(this.data);
11260 this.fireEvent("loadexception", this, arg, null, e);
11261 callback.call(scope, null, arg, false);
11264 callback.call(scope, result, arg, true);
11268 update : function(params, records){
11273 * Ext JS Library 1.1.1
11274 * Copyright(c) 2006-2007, Ext JS, LLC.
11276 * Originally Released Under LGPL - original licence link has changed is not relivant.
11279 * <script type="text/javascript">
11282 * @class Roo.data.HttpProxy
11283 * @extends Roo.data.DataProxy
11284 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11285 * configured to reference a certain URL.<br><br>
11287 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11288 * from which the running page was served.<br><br>
11290 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11292 * Be aware that to enable the browser to parse an XML document, the server must set
11293 * the Content-Type header in the HTTP response to "text/xml".
11295 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11296 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11297 * will be used to make the request.
11299 Roo.data.HttpProxy = function(conn){
11300 Roo.data.HttpProxy.superclass.constructor.call(this);
11301 // is conn a conn config or a real conn?
11303 this.useAjax = !conn || !conn.events;
11307 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11308 // thse are take from connection...
11311 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11314 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11315 * extra parameters to each request made by this object. (defaults to undefined)
11318 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11319 * to each request made by this object. (defaults to undefined)
11322 * @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)
11325 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11328 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11334 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11338 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11339 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11340 * a finer-grained basis than the DataProxy events.
11342 getConnection : function(){
11343 return this.useAjax ? Roo.Ajax : this.conn;
11347 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11348 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11349 * process that block using the passed callback.
11350 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11351 * for the request to the remote server.
11352 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11353 * object into a block of Roo.data.Records.
11354 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11355 * The function must be passed <ul>
11356 * <li>The Record block object</li>
11357 * <li>The "arg" argument from the load function</li>
11358 * <li>A boolean success indicator</li>
11360 * @param {Object} scope The scope in which to call the callback
11361 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11363 load : function(params, reader, callback, scope, arg){
11364 if(this.fireEvent("beforeload", this, params) !== false){
11366 params : params || {},
11368 callback : callback,
11373 callback : this.loadResponse,
11377 Roo.applyIf(o, this.conn);
11378 if(this.activeRequest){
11379 Roo.Ajax.abort(this.activeRequest);
11381 this.activeRequest = Roo.Ajax.request(o);
11383 this.conn.request(o);
11386 callback.call(scope||this, null, arg, false);
11391 loadResponse : function(o, success, response){
11392 delete this.activeRequest;
11394 this.fireEvent("loadexception", this, o, response);
11395 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11400 result = o.reader.read(response);
11402 this.fireEvent("loadexception", this, o, response, e);
11403 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11407 this.fireEvent("load", this, o, o.request.arg);
11408 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11412 update : function(dataSet){
11417 updateResponse : function(dataSet){
11422 * Ext JS Library 1.1.1
11423 * Copyright(c) 2006-2007, Ext JS, LLC.
11425 * Originally Released Under LGPL - original licence link has changed is not relivant.
11428 * <script type="text/javascript">
11432 * @class Roo.data.ScriptTagProxy
11433 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11434 * other than the originating domain of the running page.<br><br>
11436 * <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
11437 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11439 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11440 * source code that is used as the source inside a <script> tag.<br><br>
11442 * In order for the browser to process the returned data, the server must wrap the data object
11443 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11444 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11445 * depending on whether the callback name was passed:
11448 boolean scriptTag = false;
11449 String cb = request.getParameter("callback");
11452 response.setContentType("text/javascript");
11454 response.setContentType("application/x-json");
11456 Writer out = response.getWriter();
11458 out.write(cb + "(");
11460 out.print(dataBlock.toJsonString());
11467 * @param {Object} config A configuration object.
11469 Roo.data.ScriptTagProxy = function(config){
11470 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11471 Roo.apply(this, config);
11472 this.head = document.getElementsByTagName("head")[0];
11475 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11477 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11479 * @cfg {String} url The URL from which to request the data object.
11482 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11486 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11487 * the server the name of the callback function set up by the load call to process the returned data object.
11488 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11489 * javascript output which calls this named function passing the data object as its only parameter.
11491 callbackParam : "callback",
11493 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11494 * name to the request.
11499 * Load data from the configured URL, read the data object into
11500 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11501 * process that block using the passed callback.
11502 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11503 * for the request to the remote server.
11504 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11505 * object into a block of Roo.data.Records.
11506 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11507 * The function must be passed <ul>
11508 * <li>The Record block object</li>
11509 * <li>The "arg" argument from the load function</li>
11510 * <li>A boolean success indicator</li>
11512 * @param {Object} scope The scope in which to call the callback
11513 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11515 load : function(params, reader, callback, scope, arg){
11516 if(this.fireEvent("beforeload", this, params) !== false){
11518 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11520 var url = this.url;
11521 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11523 url += "&_dc=" + (new Date().getTime());
11525 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11528 cb : "stcCallback"+transId,
11529 scriptId : "stcScript"+transId,
11533 callback : callback,
11539 window[trans.cb] = function(o){
11540 conn.handleResponse(o, trans);
11543 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11545 if(this.autoAbort !== false){
11549 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11551 var script = document.createElement("script");
11552 script.setAttribute("src", url);
11553 script.setAttribute("type", "text/javascript");
11554 script.setAttribute("id", trans.scriptId);
11555 this.head.appendChild(script);
11557 this.trans = trans;
11559 callback.call(scope||this, null, arg, false);
11564 isLoading : function(){
11565 return this.trans ? true : false;
11569 * Abort the current server request.
11571 abort : function(){
11572 if(this.isLoading()){
11573 this.destroyTrans(this.trans);
11578 destroyTrans : function(trans, isLoaded){
11579 this.head.removeChild(document.getElementById(trans.scriptId));
11580 clearTimeout(trans.timeoutId);
11582 window[trans.cb] = undefined;
11584 delete window[trans.cb];
11587 // if hasn't been loaded, wait for load to remove it to prevent script error
11588 window[trans.cb] = function(){
11589 window[trans.cb] = undefined;
11591 delete window[trans.cb];
11598 handleResponse : function(o, trans){
11599 this.trans = false;
11600 this.destroyTrans(trans, true);
11603 result = trans.reader.readRecords(o);
11605 this.fireEvent("loadexception", this, o, trans.arg, e);
11606 trans.callback.call(trans.scope||window, null, trans.arg, false);
11609 this.fireEvent("load", this, o, trans.arg);
11610 trans.callback.call(trans.scope||window, result, trans.arg, true);
11614 handleFailure : function(trans){
11615 this.trans = false;
11616 this.destroyTrans(trans, false);
11617 this.fireEvent("loadexception", this, null, trans.arg);
11618 trans.callback.call(trans.scope||window, null, trans.arg, false);
11622 * Ext JS Library 1.1.1
11623 * Copyright(c) 2006-2007, Ext JS, LLC.
11625 * Originally Released Under LGPL - original licence link has changed is not relivant.
11628 * <script type="text/javascript">
11632 * @class Roo.data.JsonReader
11633 * @extends Roo.data.DataReader
11634 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11635 * based on mappings in a provided Roo.data.Record constructor.
11637 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11638 * in the reply previously.
11643 var RecordDef = Roo.data.Record.create([
11644 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11645 {name: 'occupation'} // This field will use "occupation" as the mapping.
11647 var myReader = new Roo.data.JsonReader({
11648 totalProperty: "results", // The property which contains the total dataset size (optional)
11649 root: "rows", // The property which contains an Array of row objects
11650 id: "id" // The property within each row object that provides an ID for the record (optional)
11654 * This would consume a JSON file like this:
11656 { 'results': 2, 'rows': [
11657 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11658 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11661 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11662 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11663 * paged from the remote server.
11664 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11665 * @cfg {String} root name of the property which contains the Array of row objects.
11666 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11667 * @cfg {Array} fields Array of field definition objects
11669 * Create a new JsonReader
11670 * @param {Object} meta Metadata configuration options
11671 * @param {Object} recordType Either an Array of field definition objects,
11672 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11674 Roo.data.JsonReader = function(meta, recordType){
11677 // set some defaults:
11678 Roo.applyIf(meta, {
11679 totalProperty: 'total',
11680 successProperty : 'success',
11685 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11687 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11690 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11691 * Used by Store query builder to append _requestMeta to params.
11694 metaFromRemote : false,
11696 * This method is only used by a DataProxy which has retrieved data from a remote server.
11697 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11698 * @return {Object} data A data block which is used by an Roo.data.Store object as
11699 * a cache of Roo.data.Records.
11701 read : function(response){
11702 var json = response.responseText;
11704 var o = /* eval:var:o */ eval("("+json+")");
11706 throw {message: "JsonReader.read: Json object not found"};
11712 this.metaFromRemote = true;
11713 this.meta = o.metaData;
11714 this.recordType = Roo.data.Record.create(o.metaData.fields);
11715 this.onMetaChange(this.meta, this.recordType, o);
11717 return this.readRecords(o);
11720 // private function a store will implement
11721 onMetaChange : function(meta, recordType, o){
11728 simpleAccess: function(obj, subsc) {
11735 getJsonAccessor: function(){
11737 return function(expr) {
11739 return(re.test(expr))
11740 ? new Function("obj", "return obj." + expr)
11745 return Roo.emptyFn;
11750 * Create a data block containing Roo.data.Records from an XML document.
11751 * @param {Object} o An object which contains an Array of row objects in the property specified
11752 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11753 * which contains the total size of the dataset.
11754 * @return {Object} data A data block which is used by an Roo.data.Store object as
11755 * a cache of Roo.data.Records.
11757 readRecords : function(o){
11759 * After any data loads, the raw JSON data is available for further custom processing.
11763 var s = this.meta, Record = this.recordType,
11764 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11766 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11768 if(s.totalProperty) {
11769 this.getTotal = this.getJsonAccessor(s.totalProperty);
11771 if(s.successProperty) {
11772 this.getSuccess = this.getJsonAccessor(s.successProperty);
11774 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11776 var g = this.getJsonAccessor(s.id);
11777 this.getId = function(rec) {
11779 return (r === undefined || r === "") ? null : r;
11782 this.getId = function(){return null;};
11785 for(var jj = 0; jj < fl; jj++){
11787 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11788 this.ef[jj] = this.getJsonAccessor(map);
11792 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11793 if(s.totalProperty){
11794 var vt = parseInt(this.getTotal(o), 10);
11799 if(s.successProperty){
11800 var vs = this.getSuccess(o);
11801 if(vs === false || vs === 'false'){
11806 for(var i = 0; i < c; i++){
11809 var id = this.getId(n);
11810 for(var j = 0; j < fl; j++){
11812 var v = this.ef[j](n);
11814 Roo.log('missing convert for ' + f.name);
11818 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11820 var record = new Record(values, id);
11822 records[i] = record;
11828 totalRecords : totalRecords
11833 * Ext JS Library 1.1.1
11834 * Copyright(c) 2006-2007, Ext JS, LLC.
11836 * Originally Released Under LGPL - original licence link has changed is not relivant.
11839 * <script type="text/javascript">
11843 * @class Roo.data.ArrayReader
11844 * @extends Roo.data.DataReader
11845 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11846 * Each element of that Array represents a row of data fields. The
11847 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11848 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11852 var RecordDef = Roo.data.Record.create([
11853 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11854 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11856 var myReader = new Roo.data.ArrayReader({
11857 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11861 * This would consume an Array like this:
11863 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11865 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11867 * Create a new JsonReader
11868 * @param {Object} meta Metadata configuration options.
11869 * @param {Object} recordType Either an Array of field definition objects
11870 * as specified to {@link Roo.data.Record#create},
11871 * or an {@link Roo.data.Record} object
11872 * created using {@link Roo.data.Record#create}.
11874 Roo.data.ArrayReader = function(meta, recordType){
11875 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11878 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11880 * Create a data block containing Roo.data.Records from an XML document.
11881 * @param {Object} o An Array of row objects which represents the dataset.
11882 * @return {Object} data A data block which is used by an Roo.data.Store object as
11883 * a cache of Roo.data.Records.
11885 readRecords : function(o){
11886 var sid = this.meta ? this.meta.id : null;
11887 var recordType = this.recordType, fields = recordType.prototype.fields;
11890 for(var i = 0; i < root.length; i++){
11893 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11894 for(var j = 0, jlen = fields.length; j < jlen; j++){
11895 var f = fields.items[j];
11896 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11897 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11899 values[f.name] = v;
11901 var record = new recordType(values, id);
11903 records[records.length] = record;
11907 totalRecords : records.length
11916 * @class Roo.bootstrap.ComboBox
11917 * @extends Roo.bootstrap.TriggerField
11918 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11919 * @cfg {Boolean} append (true|false) default false
11920 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11921 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11922 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11923 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11924 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11925 * @cfg {Boolean} animate default true
11926 * @cfg {Boolean} emptyResultText only for touch device
11927 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11929 * Create a new ComboBox.
11930 * @param {Object} config Configuration options
11932 Roo.bootstrap.ComboBox = function(config){
11933 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11937 * Fires when the dropdown list is expanded
11938 * @param {Roo.bootstrap.ComboBox} combo This combo box
11943 * Fires when the dropdown list is collapsed
11944 * @param {Roo.bootstrap.ComboBox} combo This combo box
11948 * @event beforeselect
11949 * Fires before a list item is selected. Return false to cancel the selection.
11950 * @param {Roo.bootstrap.ComboBox} combo This combo box
11951 * @param {Roo.data.Record} record The data record returned from the underlying store
11952 * @param {Number} index The index of the selected item in the dropdown list
11954 'beforeselect' : true,
11957 * Fires when a list item is selected
11958 * @param {Roo.bootstrap.ComboBox} combo This combo box
11959 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11960 * @param {Number} index The index of the selected item in the dropdown list
11964 * @event beforequery
11965 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11966 * The event object passed has these properties:
11967 * @param {Roo.bootstrap.ComboBox} combo This combo box
11968 * @param {String} query The query
11969 * @param {Boolean} forceAll true to force "all" query
11970 * @param {Boolean} cancel true to cancel the query
11971 * @param {Object} e The query event object
11973 'beforequery': true,
11976 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11977 * @param {Roo.bootstrap.ComboBox} combo This combo box
11982 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11983 * @param {Roo.bootstrap.ComboBox} combo This combo box
11984 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11989 * Fires when the remove value from the combobox array
11990 * @param {Roo.bootstrap.ComboBox} combo This combo box
11994 * @event afterremove
11995 * Fires when the remove value from the combobox array
11996 * @param {Roo.bootstrap.ComboBox} combo This combo box
11998 'afterremove' : true,
12000 * @event specialfilter
12001 * Fires when specialfilter
12002 * @param {Roo.bootstrap.ComboBox} combo This combo box
12004 'specialfilter' : true,
12007 * Fires when tick the element
12008 * @param {Roo.bootstrap.ComboBox} combo This combo box
12012 * @event touchviewdisplay
12013 * Fires when touch view require special display (default is using displayField)
12014 * @param {Roo.bootstrap.ComboBox} combo This combo box
12015 * @param {Object} cfg set html .
12017 'touchviewdisplay' : true
12022 this.tickItems = [];
12024 this.selectedIndex = -1;
12025 if(this.mode == 'local'){
12026 if(config.queryDelay === undefined){
12027 this.queryDelay = 10;
12029 if(config.minChars === undefined){
12035 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12038 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12039 * rendering into an Roo.Editor, defaults to false)
12042 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12043 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12046 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12049 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12050 * the dropdown list (defaults to undefined, with no header element)
12054 * @cfg {String/Roo.Template} tpl The template to use to render the output
12058 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12060 listWidth: undefined,
12062 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12063 * mode = 'remote' or 'text' if mode = 'local')
12065 displayField: undefined,
12068 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12069 * mode = 'remote' or 'value' if mode = 'local').
12070 * Note: use of a valueField requires the user make a selection
12071 * in order for a value to be mapped.
12073 valueField: undefined,
12075 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12080 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12081 * field's data value (defaults to the underlying DOM element's name)
12083 hiddenName: undefined,
12085 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12089 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12091 selectedClass: 'active',
12094 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12098 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12099 * anchor positions (defaults to 'tl-bl')
12101 listAlign: 'tl-bl?',
12103 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12107 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12108 * query specified by the allQuery config option (defaults to 'query')
12110 triggerAction: 'query',
12112 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12113 * (defaults to 4, does not apply if editable = false)
12117 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12118 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12122 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12123 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12127 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12128 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12132 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12133 * when editable = true (defaults to false)
12135 selectOnFocus:false,
12137 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12139 queryParam: 'query',
12141 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12142 * when mode = 'remote' (defaults to 'Loading...')
12144 loadingText: 'Loading...',
12146 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12150 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12154 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12155 * traditional select (defaults to true)
12159 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12163 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12167 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12168 * listWidth has a higher value)
12172 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12173 * allow the user to set arbitrary text into the field (defaults to false)
12175 forceSelection:false,
12177 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12178 * if typeAhead = true (defaults to 250)
12180 typeAheadDelay : 250,
12182 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12183 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12185 valueNotFoundText : undefined,
12187 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12189 blockFocus : false,
12192 * @cfg {Boolean} disableClear Disable showing of clear button.
12194 disableClear : false,
12196 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12198 alwaysQuery : false,
12201 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12206 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12208 invalidClass : "has-warning",
12211 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12213 validClass : "has-success",
12216 * @cfg {Boolean} specialFilter (true|false) special filter default false
12218 specialFilter : false,
12221 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12223 mobileTouchView : true,
12235 btnPosition : 'right',
12236 triggerList : true,
12237 showToggleBtn : true,
12239 emptyResultText: 'Empty',
12240 triggerText : 'Select',
12242 // element that contains real text value.. (when hidden is used..)
12244 getAutoCreate : function()
12252 if(Roo.isTouch && this.mobileTouchView){
12253 cfg = this.getAutoCreateTouchView();
12260 if(!this.tickable){
12261 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12266 * ComboBox with tickable selections
12269 var align = this.labelAlign || this.parentLabelAlign();
12272 cls : 'form-group roo-combobox-tickable' //input-group
12277 cls : 'tickable-buttons',
12282 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12283 html : this.triggerText
12289 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12296 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12303 buttons.cn.unshift({
12305 cls: 'roo-select2-search-field-input'
12311 Roo.each(buttons.cn, function(c){
12313 c.cls += ' btn-' + _this.size;
12316 if (_this.disabled) {
12327 cls: 'form-hidden-field'
12331 cls: 'roo-select2-choices',
12335 cls: 'roo-select2-search-field',
12347 cls: 'roo-select2-container input-group roo-select2-container-multi',
12352 // cls: 'typeahead typeahead-long dropdown-menu',
12353 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12358 if(this.hasFeedback && !this.allowBlank){
12362 cls: 'glyphicon form-control-feedback'
12365 combobox.cn.push(feedback);
12368 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12370 // Roo.log("left and has label");
12374 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12375 tooltip : 'This field is required'
12380 cls : 'control-label col-sm-' + this.labelWidth,
12381 html : this.fieldLabel
12385 cls : "col-sm-" + (12 - this.labelWidth),
12393 if(this.indicatorpos == 'right'){
12399 cls : 'control-label col-sm-' + this.labelWidth,
12400 html : this.fieldLabel
12405 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12406 tooltip : 'This field is required'
12409 cls : "col-sm-" + (12 - this.labelWidth),
12420 } else if ( this.fieldLabel.length) {
12421 // Roo.log(" label");
12425 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12426 tooltip : 'This field is required'
12430 //cls : 'input-group-addon',
12431 html : this.fieldLabel
12439 if(this.indicatorpos == 'right'){
12444 //cls : 'input-group-addon',
12445 html : this.fieldLabel
12451 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12452 tooltip : 'This field is required'
12463 // Roo.log(" no label && no align");
12470 ['xs','sm','md','lg'].map(function(size){
12471 if (settings[size]) {
12472 cfg.cls += ' col-' + size + '-' + settings[size];
12480 _initEventsCalled : false,
12483 initEvents: function()
12486 if (this._initEventsCalled) { // as we call render... prevent looping...
12489 this._initEventsCalled = true;
12492 throw "can not find store for combo";
12495 this.store = Roo.factory(this.store, Roo.data);
12497 // if we are building from html. then this element is so complex, that we can not really
12498 // use the rendered HTML.
12499 // so we have to trash and replace the previous code.
12500 if (Roo.XComponent.build_from_html) {
12502 // remove this element....
12503 var e = this.el.dom, k=0;
12504 while (e ) { e = e.previousSibling; ++k;}
12509 this.rendered = false;
12511 this.render(this.parent().getChildContainer(true), k);
12522 if(Roo.isTouch && this.mobileTouchView){
12523 this.initTouchView();
12528 this.initTickableEvents();
12532 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12534 if(this.hiddenName){
12536 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12538 this.hiddenField.dom.value =
12539 this.hiddenValue !== undefined ? this.hiddenValue :
12540 this.value !== undefined ? this.value : '';
12542 // prevent input submission
12543 this.el.dom.removeAttribute('name');
12544 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12549 // this.el.dom.setAttribute('autocomplete', 'off');
12552 var cls = 'x-combo-list';
12554 //this.list = new Roo.Layer({
12555 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12561 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12562 _this.list.setWidth(lw);
12565 this.list.on('mouseover', this.onViewOver, this);
12566 this.list.on('mousemove', this.onViewMove, this);
12568 this.list.on('scroll', this.onViewScroll, this);
12571 this.list.swallowEvent('mousewheel');
12572 this.assetHeight = 0;
12575 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12576 this.assetHeight += this.header.getHeight();
12579 this.innerList = this.list.createChild({cls:cls+'-inner'});
12580 this.innerList.on('mouseover', this.onViewOver, this);
12581 this.innerList.on('mousemove', this.onViewMove, this);
12582 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12584 if(this.allowBlank && !this.pageSize && !this.disableClear){
12585 this.footer = this.list.createChild({cls:cls+'-ft'});
12586 this.pageTb = new Roo.Toolbar(this.footer);
12590 this.footer = this.list.createChild({cls:cls+'-ft'});
12591 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12592 {pageSize: this.pageSize});
12596 if (this.pageTb && this.allowBlank && !this.disableClear) {
12598 this.pageTb.add(new Roo.Toolbar.Fill(), {
12599 cls: 'x-btn-icon x-btn-clear',
12601 handler: function()
12604 _this.clearValue();
12605 _this.onSelect(false, -1);
12610 this.assetHeight += this.footer.getHeight();
12615 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12618 this.view = new Roo.View(this.list, this.tpl, {
12619 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12621 //this.view.wrapEl.setDisplayed(false);
12622 this.view.on('click', this.onViewClick, this);
12626 this.store.on('beforeload', this.onBeforeLoad, this);
12627 this.store.on('load', this.onLoad, this);
12628 this.store.on('loadexception', this.onLoadException, this);
12630 if(this.resizable){
12631 this.resizer = new Roo.Resizable(this.list, {
12632 pinned:true, handles:'se'
12634 this.resizer.on('resize', function(r, w, h){
12635 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12636 this.listWidth = w;
12637 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12638 this.restrictHeight();
12640 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12643 if(!this.editable){
12644 this.editable = true;
12645 this.setEditable(false);
12650 if (typeof(this.events.add.listeners) != 'undefined') {
12652 this.addicon = this.wrap.createChild(
12653 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12655 this.addicon.on('click', function(e) {
12656 this.fireEvent('add', this);
12659 if (typeof(this.events.edit.listeners) != 'undefined') {
12661 this.editicon = this.wrap.createChild(
12662 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12663 if (this.addicon) {
12664 this.editicon.setStyle('margin-left', '40px');
12666 this.editicon.on('click', function(e) {
12668 // we fire even if inothing is selected..
12669 this.fireEvent('edit', this, this.lastData );
12675 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12676 "up" : function(e){
12677 this.inKeyMode = true;
12681 "down" : function(e){
12682 if(!this.isExpanded()){
12683 this.onTriggerClick();
12685 this.inKeyMode = true;
12690 "enter" : function(e){
12691 // this.onViewClick();
12695 if(this.fireEvent("specialkey", this, e)){
12696 this.onViewClick(false);
12702 "esc" : function(e){
12706 "tab" : function(e){
12709 if(this.fireEvent("specialkey", this, e)){
12710 this.onViewClick(false);
12718 doRelay : function(foo, bar, hname){
12719 if(hname == 'down' || this.scope.isExpanded()){
12720 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12729 this.queryDelay = Math.max(this.queryDelay || 10,
12730 this.mode == 'local' ? 10 : 250);
12733 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12735 if(this.typeAhead){
12736 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12738 if(this.editable !== false){
12739 this.inputEl().on("keyup", this.onKeyUp, this);
12741 if(this.forceSelection){
12742 this.inputEl().on('blur', this.doForce, this);
12746 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12747 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12751 initTickableEvents: function()
12755 if(this.hiddenName){
12757 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12759 this.hiddenField.dom.value =
12760 this.hiddenValue !== undefined ? this.hiddenValue :
12761 this.value !== undefined ? this.value : '';
12763 // prevent input submission
12764 this.el.dom.removeAttribute('name');
12765 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12770 // this.list = this.el.select('ul.dropdown-menu',true).first();
12772 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12773 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12774 if(this.triggerList){
12775 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12778 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12779 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12781 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12782 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12784 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12785 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12787 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12788 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12789 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12792 this.cancelBtn.hide();
12797 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12798 _this.list.setWidth(lw);
12801 this.list.on('mouseover', this.onViewOver, this);
12802 this.list.on('mousemove', this.onViewMove, this);
12804 this.list.on('scroll', this.onViewScroll, this);
12807 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>';
12810 this.view = new Roo.View(this.list, this.tpl, {
12811 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12814 //this.view.wrapEl.setDisplayed(false);
12815 this.view.on('click', this.onViewClick, this);
12819 this.store.on('beforeload', this.onBeforeLoad, this);
12820 this.store.on('load', this.onLoad, this);
12821 this.store.on('loadexception', this.onLoadException, this);
12824 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12825 "up" : function(e){
12826 this.inKeyMode = true;
12830 "down" : function(e){
12831 this.inKeyMode = true;
12835 "enter" : function(e){
12836 if(this.fireEvent("specialkey", this, e)){
12837 this.onViewClick(false);
12843 "esc" : function(e){
12844 this.onTickableFooterButtonClick(e, false, false);
12847 "tab" : function(e){
12848 this.fireEvent("specialkey", this, e);
12850 this.onTickableFooterButtonClick(e, false, false);
12857 doRelay : function(e, fn, key){
12858 if(this.scope.isExpanded()){
12859 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12868 this.queryDelay = Math.max(this.queryDelay || 10,
12869 this.mode == 'local' ? 10 : 250);
12872 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12874 if(this.typeAhead){
12875 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12878 if(this.editable !== false){
12879 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12884 onDestroy : function(){
12886 this.view.setStore(null);
12887 this.view.el.removeAllListeners();
12888 this.view.el.remove();
12889 this.view.purgeListeners();
12892 this.list.dom.innerHTML = '';
12896 this.store.un('beforeload', this.onBeforeLoad, this);
12897 this.store.un('load', this.onLoad, this);
12898 this.store.un('loadexception', this.onLoadException, this);
12900 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12904 fireKey : function(e){
12905 if(e.isNavKeyPress() && !this.list.isVisible()){
12906 this.fireEvent("specialkey", this, e);
12911 onResize: function(w, h){
12912 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12914 // if(typeof w != 'number'){
12915 // // we do not handle it!?!?
12918 // var tw = this.trigger.getWidth();
12919 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12920 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12922 // this.inputEl().setWidth( this.adjustWidth('input', x));
12924 // //this.trigger.setStyle('left', x+'px');
12926 // if(this.list && this.listWidth === undefined){
12927 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12928 // this.list.setWidth(lw);
12929 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12937 * Allow or prevent the user from directly editing the field text. If false is passed,
12938 * the user will only be able to select from the items defined in the dropdown list. This method
12939 * is the runtime equivalent of setting the 'editable' config option at config time.
12940 * @param {Boolean} value True to allow the user to directly edit the field text
12942 setEditable : function(value){
12943 if(value == this.editable){
12946 this.editable = value;
12948 this.inputEl().dom.setAttribute('readOnly', true);
12949 this.inputEl().on('mousedown', this.onTriggerClick, this);
12950 this.inputEl().addClass('x-combo-noedit');
12952 this.inputEl().dom.setAttribute('readOnly', false);
12953 this.inputEl().un('mousedown', this.onTriggerClick, this);
12954 this.inputEl().removeClass('x-combo-noedit');
12960 onBeforeLoad : function(combo,opts){
12961 if(!this.hasFocus){
12965 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12967 this.restrictHeight();
12968 this.selectedIndex = -1;
12972 onLoad : function(){
12974 this.hasQuery = false;
12976 if(!this.hasFocus){
12980 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12981 this.loading.hide();
12984 if(this.store.getCount() > 0){
12986 this.restrictHeight();
12987 if(this.lastQuery == this.allQuery){
12988 if(this.editable && !this.tickable){
12989 this.inputEl().dom.select();
12993 !this.selectByValue(this.value, true) &&
12996 !this.store.lastOptions ||
12997 typeof(this.store.lastOptions.add) == 'undefined' ||
12998 this.store.lastOptions.add != true
13001 this.select(0, true);
13004 if(this.autoFocus){
13007 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13008 this.taTask.delay(this.typeAheadDelay);
13012 this.onEmptyResults();
13018 onLoadException : function()
13020 this.hasQuery = false;
13022 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13023 this.loading.hide();
13026 if(this.tickable && this.editable){
13031 // only causes errors at present
13032 //Roo.log(this.store.reader.jsonData);
13033 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13035 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13041 onTypeAhead : function(){
13042 if(this.store.getCount() > 0){
13043 var r = this.store.getAt(0);
13044 var newValue = r.data[this.displayField];
13045 var len = newValue.length;
13046 var selStart = this.getRawValue().length;
13048 if(selStart != len){
13049 this.setRawValue(newValue);
13050 this.selectText(selStart, newValue.length);
13056 onSelect : function(record, index){
13058 if(this.fireEvent('beforeselect', this, record, index) !== false){
13060 this.setFromData(index > -1 ? record.data : false);
13063 this.fireEvent('select', this, record, index);
13068 * Returns the currently selected field value or empty string if no value is set.
13069 * @return {String} value The selected value
13071 getValue : function(){
13074 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13077 if(this.valueField){
13078 return typeof this.value != 'undefined' ? this.value : '';
13080 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13085 * Clears any text/value currently set in the field
13087 clearValue : function(){
13088 if(this.hiddenField){
13089 this.hiddenField.dom.value = '';
13092 this.setRawValue('');
13093 this.lastSelectionText = '';
13094 this.lastData = false;
13096 var close = this.closeTriggerEl();
13107 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13108 * will be displayed in the field. If the value does not match the data value of an existing item,
13109 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13110 * Otherwise the field will be blank (although the value will still be set).
13111 * @param {String} value The value to match
13113 setValue : function(v){
13120 if(this.valueField){
13121 var r = this.findRecord(this.valueField, v);
13123 text = r.data[this.displayField];
13124 }else if(this.valueNotFoundText !== undefined){
13125 text = this.valueNotFoundText;
13128 this.lastSelectionText = text;
13129 if(this.hiddenField){
13130 this.hiddenField.dom.value = v;
13132 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13135 var close = this.closeTriggerEl();
13138 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13144 * @property {Object} the last set data for the element
13149 * Sets the value of the field based on a object which is related to the record format for the store.
13150 * @param {Object} value the value to set as. or false on reset?
13152 setFromData : function(o){
13159 var dv = ''; // display value
13160 var vv = ''; // value value..
13162 if (this.displayField) {
13163 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13165 // this is an error condition!!!
13166 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13169 if(this.valueField){
13170 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13173 var close = this.closeTriggerEl();
13176 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13179 if(this.hiddenField){
13180 this.hiddenField.dom.value = vv;
13182 this.lastSelectionText = dv;
13183 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13187 // no hidden field.. - we store the value in 'value', but still display
13188 // display field!!!!
13189 this.lastSelectionText = dv;
13190 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13197 reset : function(){
13198 // overridden so that last data is reset..
13205 this.setValue(this.originalValue);
13206 //this.clearInvalid();
13207 this.lastData = false;
13209 this.view.clearSelections();
13215 findRecord : function(prop, value){
13217 if(this.store.getCount() > 0){
13218 this.store.each(function(r){
13219 if(r.data[prop] == value){
13229 getName: function()
13231 // returns hidden if it's set..
13232 if (!this.rendered) {return ''};
13233 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13237 onViewMove : function(e, t){
13238 this.inKeyMode = false;
13242 onViewOver : function(e, t){
13243 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13246 var item = this.view.findItemFromChild(t);
13249 var index = this.view.indexOf(item);
13250 this.select(index, false);
13255 onViewClick : function(view, doFocus, el, e)
13257 var index = this.view.getSelectedIndexes()[0];
13259 var r = this.store.getAt(index);
13263 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13270 Roo.each(this.tickItems, function(v,k){
13272 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13274 _this.tickItems.splice(k, 1);
13276 if(typeof(e) == 'undefined' && view == false){
13277 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13289 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13290 this.tickItems.push(r.data);
13293 if(typeof(e) == 'undefined' && view == false){
13294 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13301 this.onSelect(r, index);
13303 if(doFocus !== false && !this.blockFocus){
13304 this.inputEl().focus();
13309 restrictHeight : function(){
13310 //this.innerList.dom.style.height = '';
13311 //var inner = this.innerList.dom;
13312 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13313 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13314 //this.list.beginUpdate();
13315 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13316 this.list.alignTo(this.inputEl(), this.listAlign);
13317 this.list.alignTo(this.inputEl(), this.listAlign);
13318 //this.list.endUpdate();
13322 onEmptyResults : function(){
13324 if(this.tickable && this.editable){
13325 this.restrictHeight();
13333 * Returns true if the dropdown list is expanded, else false.
13335 isExpanded : function(){
13336 return this.list.isVisible();
13340 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13341 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13342 * @param {String} value The data value of the item to select
13343 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13344 * selected item if it is not currently in view (defaults to true)
13345 * @return {Boolean} True if the value matched an item in the list, else false
13347 selectByValue : function(v, scrollIntoView){
13348 if(v !== undefined && v !== null){
13349 var r = this.findRecord(this.valueField || this.displayField, v);
13351 this.select(this.store.indexOf(r), scrollIntoView);
13359 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13360 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13361 * @param {Number} index The zero-based index of the list item to select
13362 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13363 * selected item if it is not currently in view (defaults to true)
13365 select : function(index, scrollIntoView){
13366 this.selectedIndex = index;
13367 this.view.select(index);
13368 if(scrollIntoView !== false){
13369 var el = this.view.getNode(index);
13371 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13374 this.list.scrollChildIntoView(el, false);
13380 selectNext : function(){
13381 var ct = this.store.getCount();
13383 if(this.selectedIndex == -1){
13385 }else if(this.selectedIndex < ct-1){
13386 this.select(this.selectedIndex+1);
13392 selectPrev : function(){
13393 var ct = this.store.getCount();
13395 if(this.selectedIndex == -1){
13397 }else if(this.selectedIndex != 0){
13398 this.select(this.selectedIndex-1);
13404 onKeyUp : function(e){
13405 if(this.editable !== false && !e.isSpecialKey()){
13406 this.lastKey = e.getKey();
13407 this.dqTask.delay(this.queryDelay);
13412 validateBlur : function(){
13413 return !this.list || !this.list.isVisible();
13417 initQuery : function(){
13419 var v = this.getRawValue();
13421 if(this.tickable && this.editable){
13422 v = this.tickableInputEl().getValue();
13429 doForce : function(){
13430 if(this.inputEl().dom.value.length > 0){
13431 this.inputEl().dom.value =
13432 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13438 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13439 * query allowing the query action to be canceled if needed.
13440 * @param {String} query The SQL query to execute
13441 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13442 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13443 * saved in the current store (defaults to false)
13445 doQuery : function(q, forceAll){
13447 if(q === undefined || q === null){
13452 forceAll: forceAll,
13456 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13461 forceAll = qe.forceAll;
13462 if(forceAll === true || (q.length >= this.minChars)){
13464 this.hasQuery = true;
13466 if(this.lastQuery != q || this.alwaysQuery){
13467 this.lastQuery = q;
13468 if(this.mode == 'local'){
13469 this.selectedIndex = -1;
13471 this.store.clearFilter();
13474 if(this.specialFilter){
13475 this.fireEvent('specialfilter', this);
13480 this.store.filter(this.displayField, q);
13483 this.store.fireEvent("datachanged", this.store);
13490 this.store.baseParams[this.queryParam] = q;
13492 var options = {params : this.getParams(q)};
13495 options.add = true;
13496 options.params.start = this.page * this.pageSize;
13499 this.store.load(options);
13502 * this code will make the page width larger, at the beginning, the list not align correctly,
13503 * we should expand the list on onLoad
13504 * so command out it
13509 this.selectedIndex = -1;
13514 this.loadNext = false;
13518 getParams : function(q){
13520 //p[this.queryParam] = q;
13524 p.limit = this.pageSize;
13530 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13532 collapse : function(){
13533 if(!this.isExpanded()){
13540 this.hasFocus = false;
13542 this.cancelBtn.hide();
13543 this.trigger.show();
13546 this.tickableInputEl().dom.value = '';
13547 this.tickableInputEl().blur();
13552 Roo.get(document).un('mousedown', this.collapseIf, this);
13553 Roo.get(document).un('mousewheel', this.collapseIf, this);
13554 if (!this.editable) {
13555 Roo.get(document).un('keydown', this.listKeyPress, this);
13557 this.fireEvent('collapse', this);
13563 collapseIf : function(e){
13564 var in_combo = e.within(this.el);
13565 var in_list = e.within(this.list);
13566 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13568 if (in_combo || in_list || is_list) {
13569 //e.stopPropagation();
13574 this.onTickableFooterButtonClick(e, false, false);
13582 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13584 expand : function(){
13586 if(this.isExpanded() || !this.hasFocus){
13590 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13591 this.list.setWidth(lw);
13598 this.restrictHeight();
13602 this.tickItems = Roo.apply([], this.item);
13605 this.cancelBtn.show();
13606 this.trigger.hide();
13609 this.tickableInputEl().focus();
13614 Roo.get(document).on('mousedown', this.collapseIf, this);
13615 Roo.get(document).on('mousewheel', this.collapseIf, this);
13616 if (!this.editable) {
13617 Roo.get(document).on('keydown', this.listKeyPress, this);
13620 this.fireEvent('expand', this);
13624 // Implements the default empty TriggerField.onTriggerClick function
13625 onTriggerClick : function(e)
13627 Roo.log('trigger click');
13629 if(this.disabled || !this.triggerList){
13634 this.loadNext = false;
13636 if(this.isExpanded()){
13638 if (!this.blockFocus) {
13639 this.inputEl().focus();
13643 this.hasFocus = true;
13644 if(this.triggerAction == 'all') {
13645 this.doQuery(this.allQuery, true);
13647 this.doQuery(this.getRawValue());
13649 if (!this.blockFocus) {
13650 this.inputEl().focus();
13655 onTickableTriggerClick : function(e)
13662 this.loadNext = false;
13663 this.hasFocus = true;
13665 if(this.triggerAction == 'all') {
13666 this.doQuery(this.allQuery, true);
13668 this.doQuery(this.getRawValue());
13672 onSearchFieldClick : function(e)
13674 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13675 this.onTickableFooterButtonClick(e, false, false);
13679 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13684 this.loadNext = false;
13685 this.hasFocus = true;
13687 if(this.triggerAction == 'all') {
13688 this.doQuery(this.allQuery, true);
13690 this.doQuery(this.getRawValue());
13694 listKeyPress : function(e)
13696 //Roo.log('listkeypress');
13697 // scroll to first matching element based on key pres..
13698 if (e.isSpecialKey()) {
13701 var k = String.fromCharCode(e.getKey()).toUpperCase();
13704 var csel = this.view.getSelectedNodes();
13705 var cselitem = false;
13707 var ix = this.view.indexOf(csel[0]);
13708 cselitem = this.store.getAt(ix);
13709 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13715 this.store.each(function(v) {
13717 // start at existing selection.
13718 if (cselitem.id == v.id) {
13724 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13725 match = this.store.indexOf(v);
13731 if (match === false) {
13732 return true; // no more action?
13735 this.view.select(match);
13736 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13737 sn.scrollIntoView(sn.dom.parentNode, false);
13740 onViewScroll : function(e, t){
13742 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){
13746 this.hasQuery = true;
13748 this.loading = this.list.select('.loading', true).first();
13750 if(this.loading === null){
13751 this.list.createChild({
13753 cls: 'loading roo-select2-more-results roo-select2-active',
13754 html: 'Loading more results...'
13757 this.loading = this.list.select('.loading', true).first();
13759 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13761 this.loading.hide();
13764 this.loading.show();
13769 this.loadNext = true;
13771 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13776 addItem : function(o)
13778 var dv = ''; // display value
13780 if (this.displayField) {
13781 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13783 // this is an error condition!!!
13784 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13791 var choice = this.choices.createChild({
13793 cls: 'roo-select2-search-choice',
13802 cls: 'roo-select2-search-choice-close',
13807 }, this.searchField);
13809 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13811 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13819 this.inputEl().dom.value = '';
13824 onRemoveItem : function(e, _self, o)
13826 e.preventDefault();
13828 this.lastItem = Roo.apply([], this.item);
13830 var index = this.item.indexOf(o.data) * 1;
13833 Roo.log('not this item?!');
13837 this.item.splice(index, 1);
13842 this.fireEvent('remove', this, e);
13848 syncValue : function()
13850 if(!this.item.length){
13857 Roo.each(this.item, function(i){
13858 if(_this.valueField){
13859 value.push(i[_this.valueField]);
13866 this.value = value.join(',');
13868 if(this.hiddenField){
13869 this.hiddenField.dom.value = this.value;
13872 this.store.fireEvent("datachanged", this.store);
13877 clearItem : function()
13879 if(!this.multiple){
13885 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13893 if(this.tickable && !Roo.isTouch){
13894 this.view.refresh();
13898 inputEl: function ()
13900 if(Roo.isTouch && this.mobileTouchView){
13901 return this.el.select('input.form-control',true).first();
13905 return this.searchField;
13908 return this.el.select('input.form-control',true).first();
13912 onTickableFooterButtonClick : function(e, btn, el)
13914 e.preventDefault();
13916 this.lastItem = Roo.apply([], this.item);
13918 if(btn && btn.name == 'cancel'){
13919 this.tickItems = Roo.apply([], this.item);
13928 Roo.each(this.tickItems, function(o){
13936 validate : function()
13938 var v = this.getRawValue();
13941 v = this.getValue();
13944 if(this.disabled || this.allowBlank || v.length){
13949 this.markInvalid();
13953 tickableInputEl : function()
13955 if(!this.tickable || !this.editable){
13956 return this.inputEl();
13959 return this.inputEl().select('.roo-select2-search-field-input', true).first();
13963 getAutoCreateTouchView : function()
13968 cls: 'form-group' //input-group
13974 type : this.inputType,
13975 cls : 'form-control x-combo-noedit',
13976 autocomplete: 'new-password',
13977 placeholder : this.placeholder || '',
13982 input.name = this.name;
13986 input.cls += ' input-' + this.size;
13989 if (this.disabled) {
13990 input.disabled = true;
14001 inputblock.cls += ' input-group';
14003 inputblock.cn.unshift({
14005 cls : 'input-group-addon',
14010 if(this.removable && !this.multiple){
14011 inputblock.cls += ' roo-removable';
14013 inputblock.cn.push({
14016 cls : 'roo-combo-removable-btn close'
14020 if(this.hasFeedback && !this.allowBlank){
14022 inputblock.cls += ' has-feedback';
14024 inputblock.cn.push({
14026 cls: 'glyphicon form-control-feedback'
14033 inputblock.cls += (this.before) ? '' : ' input-group';
14035 inputblock.cn.push({
14037 cls : 'input-group-addon',
14048 cls: 'form-hidden-field'
14062 cls: 'form-hidden-field'
14066 cls: 'roo-select2-choices',
14070 cls: 'roo-select2-search-field',
14083 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14089 if(!this.multiple && this.showToggleBtn){
14096 if (this.caret != false) {
14099 cls: 'fa fa-' + this.caret
14106 cls : 'input-group-addon btn dropdown-toggle',
14111 cls: 'combobox-clear',
14125 combobox.cls += ' roo-select2-container-multi';
14128 var align = this.labelAlign || this.parentLabelAlign();
14132 if(this.fieldLabel.length && this.labelWidth){
14134 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14135 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14140 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14141 tooltip : 'This field is required'
14145 cls : 'control-label ' + lw,
14146 html : this.fieldLabel
14157 if(this.indicatorpos == 'right'){
14161 cls : 'control-label ' + lw,
14162 html : this.fieldLabel
14167 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14168 tooltip : 'This field is required'
14180 var settings = this;
14182 ['xs','sm','md','lg'].map(function(size){
14183 if (settings[size]) {
14184 cfg.cls += ' col-' + size + '-' + settings[size];
14191 initTouchView : function()
14193 this.renderTouchView();
14195 this.touchViewEl.on('scroll', function(){
14196 this.el.dom.scrollTop = 0;
14199 this.originalValue = this.getValue();
14201 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14203 this.inputEl().on("click", this.showTouchView, this);
14204 this.triggerEl.on("click", this.showTouchView, this);
14206 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14207 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14209 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14211 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14212 this.store.on('load', this.onTouchViewLoad, this);
14213 this.store.on('loadexception', this.onTouchViewLoadException, this);
14215 if(this.hiddenName){
14217 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14219 this.hiddenField.dom.value =
14220 this.hiddenValue !== undefined ? this.hiddenValue :
14221 this.value !== undefined ? this.value : '';
14223 this.el.dom.removeAttribute('name');
14224 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14228 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14229 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14232 if(this.removable && !this.multiple){
14233 var close = this.closeTriggerEl();
14235 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14236 close.on('click', this.removeBtnClick, this, close);
14240 * fix the bug in Safari iOS8
14242 this.inputEl().on("focus", function(e){
14243 document.activeElement.blur();
14251 renderTouchView : function()
14253 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14254 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14256 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14257 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14259 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14260 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14261 this.touchViewBodyEl.setStyle('overflow', 'auto');
14263 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14264 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14266 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14267 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14271 showTouchView : function()
14277 this.touchViewHeaderEl.hide();
14279 if(this.modalTitle.length){
14280 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14281 this.touchViewHeaderEl.show();
14284 this.touchViewEl.show();
14286 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14287 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14288 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14290 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14292 if(this.modalTitle.length){
14293 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14296 this.touchViewBodyEl.setHeight(bodyHeight);
14300 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14302 this.touchViewEl.addClass('in');
14305 this.doTouchViewQuery();
14309 hideTouchView : function()
14311 this.touchViewEl.removeClass('in');
14315 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14317 this.touchViewEl.setStyle('display', 'none');
14322 setTouchViewValue : function()
14329 Roo.each(this.tickItems, function(o){
14334 this.hideTouchView();
14337 doTouchViewQuery : function()
14346 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14350 if(!this.alwaysQuery || this.mode == 'local'){
14351 this.onTouchViewLoad();
14358 onTouchViewBeforeLoad : function(combo,opts)
14364 onTouchViewLoad : function()
14366 if(this.store.getCount() < 1){
14367 this.onTouchViewEmptyResults();
14371 this.clearTouchView();
14373 var rawValue = this.getRawValue();
14375 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14377 this.tickItems = [];
14379 this.store.data.each(function(d, rowIndex){
14380 var row = this.touchViewListGroup.createChild(template);
14382 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14383 row.addClass(d.data.cls);
14386 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14389 html : d.data[this.displayField]
14392 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14393 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14396 row.removeClass('selected');
14397 if(!this.multiple && this.valueField &&
14398 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14401 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14402 row.addClass('selected');
14405 if(this.multiple && this.valueField &&
14406 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14410 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14411 this.tickItems.push(d.data);
14414 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14418 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14420 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14422 if(this.modalTitle.length){
14423 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14426 var listHeight = this.touchViewListGroup.getHeight();
14430 if(firstChecked && listHeight > bodyHeight){
14431 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14436 onTouchViewLoadException : function()
14438 this.hideTouchView();
14441 onTouchViewEmptyResults : function()
14443 this.clearTouchView();
14445 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14447 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14451 clearTouchView : function()
14453 this.touchViewListGroup.dom.innerHTML = '';
14456 onTouchViewClick : function(e, el, o)
14458 e.preventDefault();
14461 var rowIndex = o.rowIndex;
14463 var r = this.store.getAt(rowIndex);
14465 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14467 if(!this.multiple){
14468 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14469 c.dom.removeAttribute('checked');
14472 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14474 this.setFromData(r.data);
14476 var close = this.closeTriggerEl();
14482 this.hideTouchView();
14484 this.fireEvent('select', this, r, rowIndex);
14489 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14490 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14491 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14495 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14496 this.addItem(r.data);
14497 this.tickItems.push(r.data);
14503 * @cfg {Boolean} grow
14507 * @cfg {Number} growMin
14511 * @cfg {Number} growMax
14520 Roo.apply(Roo.bootstrap.ComboBox, {
14524 cls: 'modal-header',
14546 cls: 'list-group-item',
14550 cls: 'roo-combobox-list-group-item-value'
14554 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14568 listItemCheckbox : {
14570 cls: 'list-group-item',
14574 cls: 'roo-combobox-list-group-item-value'
14578 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14594 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14599 cls: 'modal-footer',
14607 cls: 'col-xs-6 text-left',
14610 cls: 'btn btn-danger roo-touch-view-cancel',
14616 cls: 'col-xs-6 text-right',
14619 cls: 'btn btn-success roo-touch-view-ok',
14630 Roo.apply(Roo.bootstrap.ComboBox, {
14632 touchViewTemplate : {
14634 cls: 'modal fade roo-combobox-touch-view',
14638 cls: 'modal-dialog',
14639 style : 'position:fixed', // we have to fix position....
14643 cls: 'modal-content',
14645 Roo.bootstrap.ComboBox.header,
14646 Roo.bootstrap.ComboBox.body,
14647 Roo.bootstrap.ComboBox.footer
14656 * Ext JS Library 1.1.1
14657 * Copyright(c) 2006-2007, Ext JS, LLC.
14659 * Originally Released Under LGPL - original licence link has changed is not relivant.
14662 * <script type="text/javascript">
14667 * @extends Roo.util.Observable
14668 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14669 * This class also supports single and multi selection modes. <br>
14670 * Create a data model bound view:
14672 var store = new Roo.data.Store(...);
14674 var view = new Roo.View({
14676 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14678 singleSelect: true,
14679 selectedClass: "ydataview-selected",
14683 // listen for node click?
14684 view.on("click", function(vw, index, node, e){
14685 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14689 dataModel.load("foobar.xml");
14691 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14693 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14694 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14696 * Note: old style constructor is still suported (container, template, config)
14699 * Create a new View
14700 * @param {Object} config The config object
14703 Roo.View = function(config, depreciated_tpl, depreciated_config){
14705 this.parent = false;
14707 if (typeof(depreciated_tpl) == 'undefined') {
14708 // new way.. - universal constructor.
14709 Roo.apply(this, config);
14710 this.el = Roo.get(this.el);
14713 this.el = Roo.get(config);
14714 this.tpl = depreciated_tpl;
14715 Roo.apply(this, depreciated_config);
14717 this.wrapEl = this.el.wrap().wrap();
14718 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14721 if(typeof(this.tpl) == "string"){
14722 this.tpl = new Roo.Template(this.tpl);
14724 // support xtype ctors..
14725 this.tpl = new Roo.factory(this.tpl, Roo);
14729 this.tpl.compile();
14734 * @event beforeclick
14735 * Fires before a click is processed. Returns false to cancel the default action.
14736 * @param {Roo.View} this
14737 * @param {Number} index The index of the target node
14738 * @param {HTMLElement} node The target node
14739 * @param {Roo.EventObject} e The raw event object
14741 "beforeclick" : true,
14744 * Fires when a template node is clicked.
14745 * @param {Roo.View} this
14746 * @param {Number} index The index of the target node
14747 * @param {HTMLElement} node The target node
14748 * @param {Roo.EventObject} e The raw event object
14753 * Fires when a template node is double clicked.
14754 * @param {Roo.View} this
14755 * @param {Number} index The index of the target node
14756 * @param {HTMLElement} node The target node
14757 * @param {Roo.EventObject} e The raw event object
14761 * @event contextmenu
14762 * Fires when a template node is right clicked.
14763 * @param {Roo.View} this
14764 * @param {Number} index The index of the target node
14765 * @param {HTMLElement} node The target node
14766 * @param {Roo.EventObject} e The raw event object
14768 "contextmenu" : true,
14770 * @event selectionchange
14771 * Fires when the selected nodes change.
14772 * @param {Roo.View} this
14773 * @param {Array} selections Array of the selected nodes
14775 "selectionchange" : true,
14778 * @event beforeselect
14779 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14780 * @param {Roo.View} this
14781 * @param {HTMLElement} node The node to be selected
14782 * @param {Array} selections Array of currently selected nodes
14784 "beforeselect" : true,
14786 * @event preparedata
14787 * Fires on every row to render, to allow you to change the data.
14788 * @param {Roo.View} this
14789 * @param {Object} data to be rendered (change this)
14791 "preparedata" : true
14799 "click": this.onClick,
14800 "dblclick": this.onDblClick,
14801 "contextmenu": this.onContextMenu,
14805 this.selections = [];
14807 this.cmp = new Roo.CompositeElementLite([]);
14809 this.store = Roo.factory(this.store, Roo.data);
14810 this.setStore(this.store, true);
14813 if ( this.footer && this.footer.xtype) {
14815 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14817 this.footer.dataSource = this.store;
14818 this.footer.container = fctr;
14819 this.footer = Roo.factory(this.footer, Roo);
14820 fctr.insertFirst(this.el);
14822 // this is a bit insane - as the paging toolbar seems to detach the el..
14823 // dom.parentNode.parentNode.parentNode
14824 // they get detached?
14828 Roo.View.superclass.constructor.call(this);
14833 Roo.extend(Roo.View, Roo.util.Observable, {
14836 * @cfg {Roo.data.Store} store Data store to load data from.
14841 * @cfg {String|Roo.Element} el The container element.
14846 * @cfg {String|Roo.Template} tpl The template used by this View
14850 * @cfg {String} dataName the named area of the template to use as the data area
14851 * Works with domtemplates roo-name="name"
14855 * @cfg {String} selectedClass The css class to add to selected nodes
14857 selectedClass : "x-view-selected",
14859 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14864 * @cfg {String} text to display on mask (default Loading)
14868 * @cfg {Boolean} multiSelect Allow multiple selection
14870 multiSelect : false,
14872 * @cfg {Boolean} singleSelect Allow single selection
14874 singleSelect: false,
14877 * @cfg {Boolean} toggleSelect - selecting
14879 toggleSelect : false,
14882 * @cfg {Boolean} tickable - selecting
14887 * Returns the element this view is bound to.
14888 * @return {Roo.Element}
14890 getEl : function(){
14891 return this.wrapEl;
14897 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14899 refresh : function(){
14900 //Roo.log('refresh');
14903 // if we are using something like 'domtemplate', then
14904 // the what gets used is:
14905 // t.applySubtemplate(NAME, data, wrapping data..)
14906 // the outer template then get' applied with
14907 // the store 'extra data'
14908 // and the body get's added to the
14909 // roo-name="data" node?
14910 // <span class='roo-tpl-{name}'></span> ?????
14914 this.clearSelections();
14915 this.el.update("");
14917 var records = this.store.getRange();
14918 if(records.length < 1) {
14920 // is this valid?? = should it render a template??
14922 this.el.update(this.emptyText);
14926 if (this.dataName) {
14927 this.el.update(t.apply(this.store.meta)); //????
14928 el = this.el.child('.roo-tpl-' + this.dataName);
14931 for(var i = 0, len = records.length; i < len; i++){
14932 var data = this.prepareData(records[i].data, i, records[i]);
14933 this.fireEvent("preparedata", this, data, i, records[i]);
14935 var d = Roo.apply({}, data);
14938 Roo.apply(d, {'roo-id' : Roo.id()});
14942 Roo.each(this.parent.item, function(item){
14943 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14946 Roo.apply(d, {'roo-data-checked' : 'checked'});
14950 html[html.length] = Roo.util.Format.trim(
14952 t.applySubtemplate(this.dataName, d, this.store.meta) :
14959 el.update(html.join(""));
14960 this.nodes = el.dom.childNodes;
14961 this.updateIndexes(0);
14966 * Function to override to reformat the data that is sent to
14967 * the template for each node.
14968 * DEPRICATED - use the preparedata event handler.
14969 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14970 * a JSON object for an UpdateManager bound view).
14972 prepareData : function(data, index, record)
14974 this.fireEvent("preparedata", this, data, index, record);
14978 onUpdate : function(ds, record){
14979 // Roo.log('on update');
14980 this.clearSelections();
14981 var index = this.store.indexOf(record);
14982 var n = this.nodes[index];
14983 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14984 n.parentNode.removeChild(n);
14985 this.updateIndexes(index, index);
14991 onAdd : function(ds, records, index)
14993 //Roo.log(['on Add', ds, records, index] );
14994 this.clearSelections();
14995 if(this.nodes.length == 0){
14999 var n = this.nodes[index];
15000 for(var i = 0, len = records.length; i < len; i++){
15001 var d = this.prepareData(records[i].data, i, records[i]);
15003 this.tpl.insertBefore(n, d);
15006 this.tpl.append(this.el, d);
15009 this.updateIndexes(index);
15012 onRemove : function(ds, record, index){
15013 // Roo.log('onRemove');
15014 this.clearSelections();
15015 var el = this.dataName ?
15016 this.el.child('.roo-tpl-' + this.dataName) :
15019 el.dom.removeChild(this.nodes[index]);
15020 this.updateIndexes(index);
15024 * Refresh an individual node.
15025 * @param {Number} index
15027 refreshNode : function(index){
15028 this.onUpdate(this.store, this.store.getAt(index));
15031 updateIndexes : function(startIndex, endIndex){
15032 var ns = this.nodes;
15033 startIndex = startIndex || 0;
15034 endIndex = endIndex || ns.length - 1;
15035 for(var i = startIndex; i <= endIndex; i++){
15036 ns[i].nodeIndex = i;
15041 * Changes the data store this view uses and refresh the view.
15042 * @param {Store} store
15044 setStore : function(store, initial){
15045 if(!initial && this.store){
15046 this.store.un("datachanged", this.refresh);
15047 this.store.un("add", this.onAdd);
15048 this.store.un("remove", this.onRemove);
15049 this.store.un("update", this.onUpdate);
15050 this.store.un("clear", this.refresh);
15051 this.store.un("beforeload", this.onBeforeLoad);
15052 this.store.un("load", this.onLoad);
15053 this.store.un("loadexception", this.onLoad);
15057 store.on("datachanged", this.refresh, this);
15058 store.on("add", this.onAdd, this);
15059 store.on("remove", this.onRemove, this);
15060 store.on("update", this.onUpdate, this);
15061 store.on("clear", this.refresh, this);
15062 store.on("beforeload", this.onBeforeLoad, this);
15063 store.on("load", this.onLoad, this);
15064 store.on("loadexception", this.onLoad, this);
15072 * onbeforeLoad - masks the loading area.
15075 onBeforeLoad : function(store,opts)
15077 //Roo.log('onBeforeLoad');
15079 this.el.update("");
15081 this.el.mask(this.mask ? this.mask : "Loading" );
15083 onLoad : function ()
15090 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15091 * @param {HTMLElement} node
15092 * @return {HTMLElement} The template node
15094 findItemFromChild : function(node){
15095 var el = this.dataName ?
15096 this.el.child('.roo-tpl-' + this.dataName,true) :
15099 if(!node || node.parentNode == el){
15102 var p = node.parentNode;
15103 while(p && p != el){
15104 if(p.parentNode == el){
15113 onClick : function(e){
15114 var item = this.findItemFromChild(e.getTarget());
15116 var index = this.indexOf(item);
15117 if(this.onItemClick(item, index, e) !== false){
15118 this.fireEvent("click", this, index, item, e);
15121 this.clearSelections();
15126 onContextMenu : function(e){
15127 var item = this.findItemFromChild(e.getTarget());
15129 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15134 onDblClick : function(e){
15135 var item = this.findItemFromChild(e.getTarget());
15137 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15141 onItemClick : function(item, index, e)
15143 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15146 if (this.toggleSelect) {
15147 var m = this.isSelected(item) ? 'unselect' : 'select';
15150 _t[m](item, true, false);
15153 if(this.multiSelect || this.singleSelect){
15154 if(this.multiSelect && e.shiftKey && this.lastSelection){
15155 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15157 this.select(item, this.multiSelect && e.ctrlKey);
15158 this.lastSelection = item;
15161 if(!this.tickable){
15162 e.preventDefault();
15170 * Get the number of selected nodes.
15173 getSelectionCount : function(){
15174 return this.selections.length;
15178 * Get the currently selected nodes.
15179 * @return {Array} An array of HTMLElements
15181 getSelectedNodes : function(){
15182 return this.selections;
15186 * Get the indexes of the selected nodes.
15189 getSelectedIndexes : function(){
15190 var indexes = [], s = this.selections;
15191 for(var i = 0, len = s.length; i < len; i++){
15192 indexes.push(s[i].nodeIndex);
15198 * Clear all selections
15199 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15201 clearSelections : function(suppressEvent){
15202 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15203 this.cmp.elements = this.selections;
15204 this.cmp.removeClass(this.selectedClass);
15205 this.selections = [];
15206 if(!suppressEvent){
15207 this.fireEvent("selectionchange", this, this.selections);
15213 * Returns true if the passed node is selected
15214 * @param {HTMLElement/Number} node The node or node index
15215 * @return {Boolean}
15217 isSelected : function(node){
15218 var s = this.selections;
15222 node = this.getNode(node);
15223 return s.indexOf(node) !== -1;
15228 * @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
15229 * @param {Boolean} keepExisting (optional) true to keep existing selections
15230 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15232 select : function(nodeInfo, keepExisting, suppressEvent){
15233 if(nodeInfo instanceof Array){
15235 this.clearSelections(true);
15237 for(var i = 0, len = nodeInfo.length; i < len; i++){
15238 this.select(nodeInfo[i], true, true);
15242 var node = this.getNode(nodeInfo);
15243 if(!node || this.isSelected(node)){
15244 return; // already selected.
15247 this.clearSelections(true);
15250 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15251 Roo.fly(node).addClass(this.selectedClass);
15252 this.selections.push(node);
15253 if(!suppressEvent){
15254 this.fireEvent("selectionchange", this, this.selections);
15262 * @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
15263 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15264 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15266 unselect : function(nodeInfo, keepExisting, suppressEvent)
15268 if(nodeInfo instanceof Array){
15269 Roo.each(this.selections, function(s) {
15270 this.unselect(s, nodeInfo);
15274 var node = this.getNode(nodeInfo);
15275 if(!node || !this.isSelected(node)){
15276 //Roo.log("not selected");
15277 return; // not selected.
15281 Roo.each(this.selections, function(s) {
15283 Roo.fly(node).removeClass(this.selectedClass);
15290 this.selections= ns;
15291 this.fireEvent("selectionchange", this, this.selections);
15295 * Gets a template node.
15296 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15297 * @return {HTMLElement} The node or null if it wasn't found
15299 getNode : function(nodeInfo){
15300 if(typeof nodeInfo == "string"){
15301 return document.getElementById(nodeInfo);
15302 }else if(typeof nodeInfo == "number"){
15303 return this.nodes[nodeInfo];
15309 * Gets a range template nodes.
15310 * @param {Number} startIndex
15311 * @param {Number} endIndex
15312 * @return {Array} An array of nodes
15314 getNodes : function(start, end){
15315 var ns = this.nodes;
15316 start = start || 0;
15317 end = typeof end == "undefined" ? ns.length - 1 : end;
15320 for(var i = start; i <= end; i++){
15324 for(var i = start; i >= end; i--){
15332 * Finds the index of the passed node
15333 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15334 * @return {Number} The index of the node or -1
15336 indexOf : function(node){
15337 node = this.getNode(node);
15338 if(typeof node.nodeIndex == "number"){
15339 return node.nodeIndex;
15341 var ns = this.nodes;
15342 for(var i = 0, len = ns.length; i < len; i++){
15353 * based on jquery fullcalendar
15357 Roo.bootstrap = Roo.bootstrap || {};
15359 * @class Roo.bootstrap.Calendar
15360 * @extends Roo.bootstrap.Component
15361 * Bootstrap Calendar class
15362 * @cfg {Boolean} loadMask (true|false) default false
15363 * @cfg {Object} header generate the user specific header of the calendar, default false
15366 * Create a new Container
15367 * @param {Object} config The config object
15372 Roo.bootstrap.Calendar = function(config){
15373 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15377 * Fires when a date is selected
15378 * @param {DatePicker} this
15379 * @param {Date} date The selected date
15383 * @event monthchange
15384 * Fires when the displayed month changes
15385 * @param {DatePicker} this
15386 * @param {Date} date The selected month
15388 'monthchange': true,
15390 * @event evententer
15391 * Fires when mouse over an event
15392 * @param {Calendar} this
15393 * @param {event} Event
15395 'evententer': true,
15397 * @event eventleave
15398 * Fires when the mouse leaves an
15399 * @param {Calendar} this
15402 'eventleave': true,
15404 * @event eventclick
15405 * Fires when the mouse click an
15406 * @param {Calendar} this
15415 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15418 * @cfg {Number} startDay
15419 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15427 getAutoCreate : function(){
15430 var fc_button = function(name, corner, style, content ) {
15431 return Roo.apply({},{
15433 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15435 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15438 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15449 style : 'width:100%',
15456 cls : 'fc-header-left',
15458 fc_button('prev', 'left', 'arrow', '‹' ),
15459 fc_button('next', 'right', 'arrow', '›' ),
15460 { tag: 'span', cls: 'fc-header-space' },
15461 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15469 cls : 'fc-header-center',
15473 cls: 'fc-header-title',
15476 html : 'month / year'
15484 cls : 'fc-header-right',
15486 /* fc_button('month', 'left', '', 'month' ),
15487 fc_button('week', '', '', 'week' ),
15488 fc_button('day', 'right', '', 'day' )
15500 header = this.header;
15503 var cal_heads = function() {
15505 // fixme - handle this.
15507 for (var i =0; i < Date.dayNames.length; i++) {
15508 var d = Date.dayNames[i];
15511 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15512 html : d.substring(0,3)
15516 ret[0].cls += ' fc-first';
15517 ret[6].cls += ' fc-last';
15520 var cal_cell = function(n) {
15523 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15528 cls: 'fc-day-number',
15532 cls: 'fc-day-content',
15536 style: 'position: relative;' // height: 17px;
15548 var cal_rows = function() {
15551 for (var r = 0; r < 6; r++) {
15558 for (var i =0; i < Date.dayNames.length; i++) {
15559 var d = Date.dayNames[i];
15560 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15563 row.cn[0].cls+=' fc-first';
15564 row.cn[0].cn[0].style = 'min-height:90px';
15565 row.cn[6].cls+=' fc-last';
15569 ret[0].cls += ' fc-first';
15570 ret[4].cls += ' fc-prev-last';
15571 ret[5].cls += ' fc-last';
15578 cls: 'fc-border-separate',
15579 style : 'width:100%',
15587 cls : 'fc-first fc-last',
15605 cls : 'fc-content',
15606 style : "position: relative;",
15609 cls : 'fc-view fc-view-month fc-grid',
15610 style : 'position: relative',
15611 unselectable : 'on',
15614 cls : 'fc-event-container',
15615 style : 'position:absolute;z-index:8;top:0;left:0;'
15633 initEvents : function()
15636 throw "can not find store for calendar";
15642 style: "text-align:center",
15646 style: "background-color:white;width:50%;margin:250 auto",
15650 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15661 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15663 var size = this.el.select('.fc-content', true).first().getSize();
15664 this.maskEl.setSize(size.width, size.height);
15665 this.maskEl.enableDisplayMode("block");
15666 if(!this.loadMask){
15667 this.maskEl.hide();
15670 this.store = Roo.factory(this.store, Roo.data);
15671 this.store.on('load', this.onLoad, this);
15672 this.store.on('beforeload', this.onBeforeLoad, this);
15676 this.cells = this.el.select('.fc-day',true);
15677 //Roo.log(this.cells);
15678 this.textNodes = this.el.query('.fc-day-number');
15679 this.cells.addClassOnOver('fc-state-hover');
15681 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15682 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15683 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15684 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15686 this.on('monthchange', this.onMonthChange, this);
15688 this.update(new Date().clearTime());
15691 resize : function() {
15692 var sz = this.el.getSize();
15694 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15695 this.el.select('.fc-day-content div',true).setHeight(34);
15700 showPrevMonth : function(e){
15701 this.update(this.activeDate.add("mo", -1));
15703 showToday : function(e){
15704 this.update(new Date().clearTime());
15707 showNextMonth : function(e){
15708 this.update(this.activeDate.add("mo", 1));
15712 showPrevYear : function(){
15713 this.update(this.activeDate.add("y", -1));
15717 showNextYear : function(){
15718 this.update(this.activeDate.add("y", 1));
15723 update : function(date)
15725 var vd = this.activeDate;
15726 this.activeDate = date;
15727 // if(vd && this.el){
15728 // var t = date.getTime();
15729 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15730 // Roo.log('using add remove');
15732 // this.fireEvent('monthchange', this, date);
15734 // this.cells.removeClass("fc-state-highlight");
15735 // this.cells.each(function(c){
15736 // if(c.dateValue == t){
15737 // c.addClass("fc-state-highlight");
15738 // setTimeout(function(){
15739 // try{c.dom.firstChild.focus();}catch(e){}
15749 var days = date.getDaysInMonth();
15751 var firstOfMonth = date.getFirstDateOfMonth();
15752 var startingPos = firstOfMonth.getDay()-this.startDay;
15754 if(startingPos < this.startDay){
15758 var pm = date.add(Date.MONTH, -1);
15759 var prevStart = pm.getDaysInMonth()-startingPos;
15761 this.cells = this.el.select('.fc-day',true);
15762 this.textNodes = this.el.query('.fc-day-number');
15763 this.cells.addClassOnOver('fc-state-hover');
15765 var cells = this.cells.elements;
15766 var textEls = this.textNodes;
15768 Roo.each(cells, function(cell){
15769 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15772 days += startingPos;
15774 // convert everything to numbers so it's fast
15775 var day = 86400000;
15776 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15779 //Roo.log(prevStart);
15781 var today = new Date().clearTime().getTime();
15782 var sel = date.clearTime().getTime();
15783 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15784 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15785 var ddMatch = this.disabledDatesRE;
15786 var ddText = this.disabledDatesText;
15787 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15788 var ddaysText = this.disabledDaysText;
15789 var format = this.format;
15791 var setCellClass = function(cal, cell){
15795 //Roo.log('set Cell Class');
15797 var t = d.getTime();
15801 cell.dateValue = t;
15803 cell.className += " fc-today";
15804 cell.className += " fc-state-highlight";
15805 cell.title = cal.todayText;
15808 // disable highlight in other month..
15809 //cell.className += " fc-state-highlight";
15814 cell.className = " fc-state-disabled";
15815 cell.title = cal.minText;
15819 cell.className = " fc-state-disabled";
15820 cell.title = cal.maxText;
15824 if(ddays.indexOf(d.getDay()) != -1){
15825 cell.title = ddaysText;
15826 cell.className = " fc-state-disabled";
15829 if(ddMatch && format){
15830 var fvalue = d.dateFormat(format);
15831 if(ddMatch.test(fvalue)){
15832 cell.title = ddText.replace("%0", fvalue);
15833 cell.className = " fc-state-disabled";
15837 if (!cell.initialClassName) {
15838 cell.initialClassName = cell.dom.className;
15841 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15846 for(; i < startingPos; i++) {
15847 textEls[i].innerHTML = (++prevStart);
15848 d.setDate(d.getDate()+1);
15850 cells[i].className = "fc-past fc-other-month";
15851 setCellClass(this, cells[i]);
15856 for(; i < days; i++){
15857 intDay = i - startingPos + 1;
15858 textEls[i].innerHTML = (intDay);
15859 d.setDate(d.getDate()+1);
15861 cells[i].className = ''; // "x-date-active";
15862 setCellClass(this, cells[i]);
15866 for(; i < 42; i++) {
15867 textEls[i].innerHTML = (++extraDays);
15868 d.setDate(d.getDate()+1);
15870 cells[i].className = "fc-future fc-other-month";
15871 setCellClass(this, cells[i]);
15874 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15876 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15878 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15879 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15881 if(totalRows != 6){
15882 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15883 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15886 this.fireEvent('monthchange', this, date);
15890 if(!this.internalRender){
15891 var main = this.el.dom.firstChild;
15892 var w = main.offsetWidth;
15893 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15894 Roo.fly(main).setWidth(w);
15895 this.internalRender = true;
15896 // opera does not respect the auto grow header center column
15897 // then, after it gets a width opera refuses to recalculate
15898 // without a second pass
15899 if(Roo.isOpera && !this.secondPass){
15900 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15901 this.secondPass = true;
15902 this.update.defer(10, this, [date]);
15909 findCell : function(dt) {
15910 dt = dt.clearTime().getTime();
15912 this.cells.each(function(c){
15913 //Roo.log("check " +c.dateValue + '?=' + dt);
15914 if(c.dateValue == dt){
15924 findCells : function(ev) {
15925 var s = ev.start.clone().clearTime().getTime();
15927 var e= ev.end.clone().clearTime().getTime();
15930 this.cells.each(function(c){
15931 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15933 if(c.dateValue > e){
15936 if(c.dateValue < s){
15945 // findBestRow: function(cells)
15949 // for (var i =0 ; i < cells.length;i++) {
15950 // ret = Math.max(cells[i].rows || 0,ret);
15957 addItem : function(ev)
15959 // look for vertical location slot in
15960 var cells = this.findCells(ev);
15962 // ev.row = this.findBestRow(cells);
15964 // work out the location.
15968 for(var i =0; i < cells.length; i++) {
15970 cells[i].row = cells[0].row;
15973 cells[i].row = cells[i].row + 1;
15983 if (crow.start.getY() == cells[i].getY()) {
15985 crow.end = cells[i];
16002 cells[0].events.push(ev);
16004 this.calevents.push(ev);
16007 clearEvents: function() {
16009 if(!this.calevents){
16013 Roo.each(this.cells.elements, function(c){
16019 Roo.each(this.calevents, function(e) {
16020 Roo.each(e.els, function(el) {
16021 el.un('mouseenter' ,this.onEventEnter, this);
16022 el.un('mouseleave' ,this.onEventLeave, this);
16027 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16033 renderEvents: function()
16037 this.cells.each(function(c) {
16046 if(c.row != c.events.length){
16047 r = 4 - (4 - (c.row - c.events.length));
16050 c.events = ev.slice(0, r);
16051 c.more = ev.slice(r);
16053 if(c.more.length && c.more.length == 1){
16054 c.events.push(c.more.pop());
16057 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16061 this.cells.each(function(c) {
16063 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16066 for (var e = 0; e < c.events.length; e++){
16067 var ev = c.events[e];
16068 var rows = ev.rows;
16070 for(var i = 0; i < rows.length; i++) {
16072 // how many rows should it span..
16075 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16076 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16078 unselectable : "on",
16081 cls: 'fc-event-inner',
16085 // cls: 'fc-event-time',
16086 // html : cells.length > 1 ? '' : ev.time
16090 cls: 'fc-event-title',
16091 html : String.format('{0}', ev.title)
16098 cls: 'ui-resizable-handle ui-resizable-e',
16099 html : '  '
16106 cfg.cls += ' fc-event-start';
16108 if ((i+1) == rows.length) {
16109 cfg.cls += ' fc-event-end';
16112 var ctr = _this.el.select('.fc-event-container',true).first();
16113 var cg = ctr.createChild(cfg);
16115 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16116 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16118 var r = (c.more.length) ? 1 : 0;
16119 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16120 cg.setWidth(ebox.right - sbox.x -2);
16122 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16123 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16124 cg.on('click', _this.onEventClick, _this, ev);
16135 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16136 style : 'position: absolute',
16137 unselectable : "on",
16140 cls: 'fc-event-inner',
16144 cls: 'fc-event-title',
16152 cls: 'ui-resizable-handle ui-resizable-e',
16153 html : '  '
16159 var ctr = _this.el.select('.fc-event-container',true).first();
16160 var cg = ctr.createChild(cfg);
16162 var sbox = c.select('.fc-day-content',true).first().getBox();
16163 var ebox = c.select('.fc-day-content',true).first().getBox();
16165 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16166 cg.setWidth(ebox.right - sbox.x -2);
16168 cg.on('click', _this.onMoreEventClick, _this, c.more);
16178 onEventEnter: function (e, el,event,d) {
16179 this.fireEvent('evententer', this, el, event);
16182 onEventLeave: function (e, el,event,d) {
16183 this.fireEvent('eventleave', this, el, event);
16186 onEventClick: function (e, el,event,d) {
16187 this.fireEvent('eventclick', this, el, event);
16190 onMonthChange: function () {
16194 onMoreEventClick: function(e, el, more)
16198 this.calpopover.placement = 'right';
16199 this.calpopover.setTitle('More');
16201 this.calpopover.setContent('');
16203 var ctr = this.calpopover.el.select('.popover-content', true).first();
16205 Roo.each(more, function(m){
16207 cls : 'fc-event-hori fc-event-draggable',
16210 var cg = ctr.createChild(cfg);
16212 cg.on('click', _this.onEventClick, _this, m);
16215 this.calpopover.show(el);
16220 onLoad: function ()
16222 this.calevents = [];
16225 if(this.store.getCount() > 0){
16226 this.store.data.each(function(d){
16229 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16230 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16231 time : d.data.start_time,
16232 title : d.data.title,
16233 description : d.data.description,
16234 venue : d.data.venue
16239 this.renderEvents();
16241 if(this.calevents.length && this.loadMask){
16242 this.maskEl.hide();
16246 onBeforeLoad: function()
16248 this.clearEvents();
16250 this.maskEl.show();
16264 * @class Roo.bootstrap.Popover
16265 * @extends Roo.bootstrap.Component
16266 * Bootstrap Popover class
16267 * @cfg {String} html contents of the popover (or false to use children..)
16268 * @cfg {String} title of popover (or false to hide)
16269 * @cfg {String} placement how it is placed
16270 * @cfg {String} trigger click || hover (or false to trigger manually)
16271 * @cfg {String} over what (parent or false to trigger manually.)
16272 * @cfg {Number} delay - delay before showing
16275 * Create a new Popover
16276 * @param {Object} config The config object
16279 Roo.bootstrap.Popover = function(config){
16280 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16286 * After the popover show
16288 * @param {Roo.bootstrap.Popover} this
16293 * After the popover hide
16295 * @param {Roo.bootstrap.Popover} this
16301 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16303 title: 'Fill in a title',
16306 placement : 'right',
16307 trigger : 'hover', // hover
16313 can_build_overlaid : false,
16315 getChildContainer : function()
16317 return this.el.select('.popover-content',true).first();
16320 getAutoCreate : function(){
16323 cls : 'popover roo-dynamic',
16324 style: 'display:block',
16330 cls : 'popover-inner',
16334 cls: 'popover-title',
16338 cls : 'popover-content',
16349 setTitle: function(str)
16352 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16354 setContent: function(str)
16357 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16359 // as it get's added to the bottom of the page.
16360 onRender : function(ct, position)
16362 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16364 var cfg = Roo.apply({}, this.getAutoCreate());
16368 cfg.cls += ' ' + this.cls;
16371 cfg.style = this.style;
16373 //Roo.log("adding to ");
16374 this.el = Roo.get(document.body).createChild(cfg, position);
16375 // Roo.log(this.el);
16380 initEvents : function()
16382 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16383 this.el.enableDisplayMode('block');
16385 if (this.over === false) {
16388 if (this.triggers === false) {
16391 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16392 var triggers = this.trigger ? this.trigger.split(' ') : [];
16393 Roo.each(triggers, function(trigger) {
16395 if (trigger == 'click') {
16396 on_el.on('click', this.toggle, this);
16397 } else if (trigger != 'manual') {
16398 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16399 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16401 on_el.on(eventIn ,this.enter, this);
16402 on_el.on(eventOut, this.leave, this);
16413 toggle : function () {
16414 this.hoverState == 'in' ? this.leave() : this.enter();
16417 enter : function () {
16419 clearTimeout(this.timeout);
16421 this.hoverState = 'in';
16423 if (!this.delay || !this.delay.show) {
16428 this.timeout = setTimeout(function () {
16429 if (_t.hoverState == 'in') {
16432 }, this.delay.show)
16435 leave : function() {
16436 clearTimeout(this.timeout);
16438 this.hoverState = 'out';
16440 if (!this.delay || !this.delay.hide) {
16445 this.timeout = setTimeout(function () {
16446 if (_t.hoverState == 'out') {
16449 }, this.delay.hide)
16452 show : function (on_el)
16455 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16459 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16460 if (this.html !== false) {
16461 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16463 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16464 if (!this.title.length) {
16465 this.el.select('.popover-title',true).hide();
16468 var placement = typeof this.placement == 'function' ?
16469 this.placement.call(this, this.el, on_el) :
16472 var autoToken = /\s?auto?\s?/i;
16473 var autoPlace = autoToken.test(placement);
16475 placement = placement.replace(autoToken, '') || 'top';
16479 //this.el.setXY([0,0]);
16481 this.el.dom.style.display='block';
16482 this.el.addClass(placement);
16484 //this.el.appendTo(on_el);
16486 var p = this.getPosition();
16487 var box = this.el.getBox();
16492 var align = Roo.bootstrap.Popover.alignment[placement];
16493 this.el.alignTo(on_el, align[0],align[1]);
16494 //var arrow = this.el.select('.arrow',true).first();
16495 //arrow.set(align[2],
16497 this.el.addClass('in');
16500 if (this.el.hasClass('fade')) {
16504 this.hoverState = 'in';
16506 this.fireEvent('show', this);
16511 this.el.setXY([0,0]);
16512 this.el.removeClass('in');
16514 this.hoverState = null;
16516 this.fireEvent('hide', this);
16521 Roo.bootstrap.Popover.alignment = {
16522 'left' : ['r-l', [-10,0], 'right'],
16523 'right' : ['l-r', [10,0], 'left'],
16524 'bottom' : ['t-b', [0,10], 'top'],
16525 'top' : [ 'b-t', [0,-10], 'bottom']
16536 * @class Roo.bootstrap.Progress
16537 * @extends Roo.bootstrap.Component
16538 * Bootstrap Progress class
16539 * @cfg {Boolean} striped striped of the progress bar
16540 * @cfg {Boolean} active animated of the progress bar
16544 * Create a new Progress
16545 * @param {Object} config The config object
16548 Roo.bootstrap.Progress = function(config){
16549 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16552 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16557 getAutoCreate : function(){
16565 cfg.cls += ' progress-striped';
16569 cfg.cls += ' active';
16588 * @class Roo.bootstrap.ProgressBar
16589 * @extends Roo.bootstrap.Component
16590 * Bootstrap ProgressBar class
16591 * @cfg {Number} aria_valuenow aria-value now
16592 * @cfg {Number} aria_valuemin aria-value min
16593 * @cfg {Number} aria_valuemax aria-value max
16594 * @cfg {String} label label for the progress bar
16595 * @cfg {String} panel (success | info | warning | danger )
16596 * @cfg {String} role role of the progress bar
16597 * @cfg {String} sr_only text
16601 * Create a new ProgressBar
16602 * @param {Object} config The config object
16605 Roo.bootstrap.ProgressBar = function(config){
16606 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16609 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16613 aria_valuemax : 100,
16619 getAutoCreate : function()
16624 cls: 'progress-bar',
16625 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16637 cfg.role = this.role;
16640 if(this.aria_valuenow){
16641 cfg['aria-valuenow'] = this.aria_valuenow;
16644 if(this.aria_valuemin){
16645 cfg['aria-valuemin'] = this.aria_valuemin;
16648 if(this.aria_valuemax){
16649 cfg['aria-valuemax'] = this.aria_valuemax;
16652 if(this.label && !this.sr_only){
16653 cfg.html = this.label;
16657 cfg.cls += ' progress-bar-' + this.panel;
16663 update : function(aria_valuenow)
16665 this.aria_valuenow = aria_valuenow;
16667 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16682 * @class Roo.bootstrap.TabGroup
16683 * @extends Roo.bootstrap.Column
16684 * Bootstrap Column class
16685 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16686 * @cfg {Boolean} carousel true to make the group behave like a carousel
16687 * @cfg {Boolean} bullets show bullets for the panels
16688 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16689 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16690 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16691 * @cfg {Boolean} showarrow (true|false) show arrow default true
16694 * Create a new TabGroup
16695 * @param {Object} config The config object
16698 Roo.bootstrap.TabGroup = function(config){
16699 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16701 this.navId = Roo.id();
16704 Roo.bootstrap.TabGroup.register(this);
16708 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16711 transition : false,
16716 slideOnTouch : false,
16719 getAutoCreate : function()
16721 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16723 cfg.cls += ' tab-content';
16725 if (this.carousel) {
16726 cfg.cls += ' carousel slide';
16729 cls : 'carousel-inner',
16733 if(this.bullets && !Roo.isTouch){
16736 cls : 'carousel-bullets',
16740 if(this.bullets_cls){
16741 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16748 cfg.cn[0].cn.push(bullets);
16751 if(this.showarrow){
16752 cfg.cn[0].cn.push({
16754 class : 'carousel-arrow',
16758 class : 'carousel-prev',
16762 class : 'fa fa-chevron-left'
16768 class : 'carousel-next',
16772 class : 'fa fa-chevron-right'
16785 initEvents: function()
16787 if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16788 this.el.on("touchstart", this.onTouchStart, this);
16791 if(this.autoslide){
16794 this.slideFn = window.setInterval(function() {
16795 _this.showPanelNext();
16799 if(this.showarrow){
16800 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16801 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16807 onTouchStart : function(e, el, o)
16809 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16813 this.showPanelNext();
16816 getChildContainer : function()
16818 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16822 * register a Navigation item
16823 * @param {Roo.bootstrap.NavItem} the navitem to add
16825 register : function(item)
16827 this.tabs.push( item);
16828 item.navId = this.navId; // not really needed..
16833 getActivePanel : function()
16836 Roo.each(this.tabs, function(t) {
16846 getPanelByName : function(n)
16849 Roo.each(this.tabs, function(t) {
16850 if (t.tabId == n) {
16858 indexOfPanel : function(p)
16861 Roo.each(this.tabs, function(t,i) {
16862 if (t.tabId == p.tabId) {
16871 * show a specific panel
16872 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16873 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16875 showPanel : function (pan)
16877 if(this.transition || typeof(pan) == 'undefined'){
16878 Roo.log("waiting for the transitionend");
16882 if (typeof(pan) == 'number') {
16883 pan = this.tabs[pan];
16886 if (typeof(pan) == 'string') {
16887 pan = this.getPanelByName(pan);
16890 var cur = this.getActivePanel();
16893 Roo.log('pan or acitve pan is undefined');
16897 if (pan.tabId == this.getActivePanel().tabId) {
16901 if (false === cur.fireEvent('beforedeactivate')) {
16905 if(this.bullets > 0 && !Roo.isTouch){
16906 this.setActiveBullet(this.indexOfPanel(pan));
16909 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16911 this.transition = true;
16912 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16913 var lr = dir == 'next' ? 'left' : 'right';
16914 pan.el.addClass(dir); // or prev
16915 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16916 cur.el.addClass(lr); // or right
16917 pan.el.addClass(lr);
16920 cur.el.on('transitionend', function() {
16921 Roo.log("trans end?");
16923 pan.el.removeClass([lr,dir]);
16924 pan.setActive(true);
16926 cur.el.removeClass([lr]);
16927 cur.setActive(false);
16929 _this.transition = false;
16931 }, this, { single: true } );
16936 cur.setActive(false);
16937 pan.setActive(true);
16942 showPanelNext : function()
16944 var i = this.indexOfPanel(this.getActivePanel());
16946 if (i >= this.tabs.length - 1 && !this.autoslide) {
16950 if (i >= this.tabs.length - 1 && this.autoslide) {
16954 this.showPanel(this.tabs[i+1]);
16957 showPanelPrev : function()
16959 var i = this.indexOfPanel(this.getActivePanel());
16961 if (i < 1 && !this.autoslide) {
16965 if (i < 1 && this.autoslide) {
16966 i = this.tabs.length;
16969 this.showPanel(this.tabs[i-1]);
16973 addBullet: function()
16975 if(!this.bullets || Roo.isTouch){
16978 var ctr = this.el.select('.carousel-bullets',true).first();
16979 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16980 var bullet = ctr.createChild({
16981 cls : 'bullet bullet-' + i
16982 },ctr.dom.lastChild);
16987 bullet.on('click', (function(e, el, o, ii, t){
16989 e.preventDefault();
16991 this.showPanel(ii);
16993 if(this.autoslide && this.slideFn){
16994 clearInterval(this.slideFn);
16995 this.slideFn = window.setInterval(function() {
16996 _this.showPanelNext();
17000 }).createDelegate(this, [i, bullet], true));
17005 setActiveBullet : function(i)
17011 Roo.each(this.el.select('.bullet', true).elements, function(el){
17012 el.removeClass('selected');
17015 var bullet = this.el.select('.bullet-' + i, true).first();
17021 bullet.addClass('selected');
17032 Roo.apply(Roo.bootstrap.TabGroup, {
17036 * register a Navigation Group
17037 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17039 register : function(navgrp)
17041 this.groups[navgrp.navId] = navgrp;
17045 * fetch a Navigation Group based on the navigation ID
17046 * if one does not exist , it will get created.
17047 * @param {string} the navgroup to add
17048 * @returns {Roo.bootstrap.NavGroup} the navgroup
17050 get: function(navId) {
17051 if (typeof(this.groups[navId]) == 'undefined') {
17052 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17054 return this.groups[navId] ;
17069 * @class Roo.bootstrap.TabPanel
17070 * @extends Roo.bootstrap.Component
17071 * Bootstrap TabPanel class
17072 * @cfg {Boolean} active panel active
17073 * @cfg {String} html panel content
17074 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17075 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17076 * @cfg {String} href click to link..
17080 * Create a new TabPanel
17081 * @param {Object} config The config object
17084 Roo.bootstrap.TabPanel = function(config){
17085 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17089 * Fires when the active status changes
17090 * @param {Roo.bootstrap.TabPanel} this
17091 * @param {Boolean} state the new state
17096 * @event beforedeactivate
17097 * Fires before a tab is de-activated - can be used to do validation on a form.
17098 * @param {Roo.bootstrap.TabPanel} this
17099 * @return {Boolean} false if there is an error
17102 'beforedeactivate': true
17105 this.tabId = this.tabId || Roo.id();
17109 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17117 getAutoCreate : function(){
17120 // item is needed for carousel - not sure if it has any effect otherwise
17121 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17122 html: this.html || ''
17126 cfg.cls += ' active';
17130 cfg.tabId = this.tabId;
17137 initEvents: function()
17139 var p = this.parent();
17140 this.navId = this.navId || p.navId;
17142 if (typeof(this.navId) != 'undefined') {
17143 // not really needed.. but just in case.. parent should be a NavGroup.
17144 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17148 var i = tg.tabs.length - 1;
17150 if(this.active && tg.bullets > 0 && i < tg.bullets){
17151 tg.setActiveBullet(i);
17155 if(this.href.length){
17156 this.el.on('click', this.onClick, this);
17161 onRender : function(ct, position)
17163 // Roo.log("Call onRender: " + this.xtype);
17165 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17173 setActive: function(state)
17175 Roo.log("panel - set active " + this.tabId + "=" + state);
17177 this.active = state;
17179 this.el.removeClass('active');
17181 } else if (!this.el.hasClass('active')) {
17182 this.el.addClass('active');
17185 this.fireEvent('changed', this, state);
17188 onClick: function(e)
17190 e.preventDefault();
17192 window.location.href = this.href;
17209 * @class Roo.bootstrap.DateField
17210 * @extends Roo.bootstrap.Input
17211 * Bootstrap DateField class
17212 * @cfg {Number} weekStart default 0
17213 * @cfg {String} viewMode default empty, (months|years)
17214 * @cfg {String} minViewMode default empty, (months|years)
17215 * @cfg {Number} startDate default -Infinity
17216 * @cfg {Number} endDate default Infinity
17217 * @cfg {Boolean} todayHighlight default false
17218 * @cfg {Boolean} todayBtn default false
17219 * @cfg {Boolean} calendarWeeks default false
17220 * @cfg {Object} daysOfWeekDisabled default empty
17221 * @cfg {Boolean} singleMode default false (true | false)
17223 * @cfg {Boolean} keyboardNavigation default true
17224 * @cfg {String} language default en
17227 * Create a new DateField
17228 * @param {Object} config The config object
17231 Roo.bootstrap.DateField = function(config){
17232 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17236 * Fires when this field show.
17237 * @param {Roo.bootstrap.DateField} this
17238 * @param {Mixed} date The date value
17243 * Fires when this field hide.
17244 * @param {Roo.bootstrap.DateField} this
17245 * @param {Mixed} date The date value
17250 * Fires when select a date.
17251 * @param {Roo.bootstrap.DateField} this
17252 * @param {Mixed} date The date value
17256 * @event beforeselect
17257 * Fires when before select a date.
17258 * @param {Roo.bootstrap.DateField} this
17259 * @param {Mixed} date The date value
17261 beforeselect : true
17265 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
17268 * @cfg {String} format
17269 * The default date format string which can be overriden for localization support. The format must be
17270 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17274 * @cfg {String} altFormats
17275 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17276 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17278 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17286 todayHighlight : false,
17292 keyboardNavigation: true,
17294 calendarWeeks: false,
17296 startDate: -Infinity,
17300 daysOfWeekDisabled: [],
17304 singleMode : false,
17306 UTCDate: function()
17308 return new Date(Date.UTC.apply(Date, arguments));
17311 UTCToday: function()
17313 var today = new Date();
17314 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17317 getDate: function() {
17318 var d = this.getUTCDate();
17319 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17322 getUTCDate: function() {
17326 setDate: function(d) {
17327 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17330 setUTCDate: function(d) {
17332 this.setValue(this.formatDate(this.date));
17335 onRender: function(ct, position)
17338 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17340 this.language = this.language || 'en';
17341 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17342 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17344 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17345 this.format = this.format || 'm/d/y';
17346 this.isInline = false;
17347 this.isInput = true;
17348 this.component = this.el.select('.add-on', true).first() || false;
17349 this.component = (this.component && this.component.length === 0) ? false : this.component;
17350 this.hasInput = this.component && this.inputEl().length;
17352 if (typeof(this.minViewMode === 'string')) {
17353 switch (this.minViewMode) {
17355 this.minViewMode = 1;
17358 this.minViewMode = 2;
17361 this.minViewMode = 0;
17366 if (typeof(this.viewMode === 'string')) {
17367 switch (this.viewMode) {
17380 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17382 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17384 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17386 this.picker().on('mousedown', this.onMousedown, this);
17387 this.picker().on('click', this.onClick, this);
17389 this.picker().addClass('datepicker-dropdown');
17391 this.startViewMode = this.viewMode;
17393 if(this.singleMode){
17394 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17395 v.setVisibilityMode(Roo.Element.DISPLAY);
17399 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17400 v.setStyle('width', '189px');
17404 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17405 if(!this.calendarWeeks){
17410 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17411 v.attr('colspan', function(i, val){
17412 return parseInt(val) + 1;
17417 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17419 this.setStartDate(this.startDate);
17420 this.setEndDate(this.endDate);
17422 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17429 if(this.isInline) {
17434 picker : function()
17436 return this.pickerEl;
17437 // return this.el.select('.datepicker', true).first();
17440 fillDow: function()
17442 var dowCnt = this.weekStart;
17451 if(this.calendarWeeks){
17459 while (dowCnt < this.weekStart + 7) {
17463 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17467 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17470 fillMonths: function()
17473 var months = this.picker().select('>.datepicker-months td', true).first();
17475 months.dom.innerHTML = '';
17481 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17484 months.createChild(month);
17491 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;
17493 if (this.date < this.startDate) {
17494 this.viewDate = new Date(this.startDate);
17495 } else if (this.date > this.endDate) {
17496 this.viewDate = new Date(this.endDate);
17498 this.viewDate = new Date(this.date);
17506 var d = new Date(this.viewDate),
17507 year = d.getUTCFullYear(),
17508 month = d.getUTCMonth(),
17509 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17510 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17511 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17512 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17513 currentDate = this.date && this.date.valueOf(),
17514 today = this.UTCToday();
17516 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17518 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17520 // this.picker.select('>tfoot th.today').
17521 // .text(dates[this.language].today)
17522 // .toggle(this.todayBtn !== false);
17524 this.updateNavArrows();
17527 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17529 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17531 prevMonth.setUTCDate(day);
17533 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17535 var nextMonth = new Date(prevMonth);
17537 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17539 nextMonth = nextMonth.valueOf();
17541 var fillMonths = false;
17543 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17545 while(prevMonth.valueOf() < nextMonth) {
17548 if (prevMonth.getUTCDay() === this.weekStart) {
17550 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17558 if(this.calendarWeeks){
17559 // ISO 8601: First week contains first thursday.
17560 // ISO also states week starts on Monday, but we can be more abstract here.
17562 // Start of current week: based on weekstart/current date
17563 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17564 // Thursday of this week
17565 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17566 // First Thursday of year, year from thursday
17567 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17568 // Calendar week: ms between thursdays, div ms per day, div 7 days
17569 calWeek = (th - yth) / 864e5 / 7 + 1;
17571 fillMonths.cn.push({
17579 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17581 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17584 if (this.todayHighlight &&
17585 prevMonth.getUTCFullYear() == today.getFullYear() &&
17586 prevMonth.getUTCMonth() == today.getMonth() &&
17587 prevMonth.getUTCDate() == today.getDate()) {
17588 clsName += ' today';
17591 if (currentDate && prevMonth.valueOf() === currentDate) {
17592 clsName += ' active';
17595 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17596 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17597 clsName += ' disabled';
17600 fillMonths.cn.push({
17602 cls: 'day ' + clsName,
17603 html: prevMonth.getDate()
17606 prevMonth.setDate(prevMonth.getDate()+1);
17609 var currentYear = this.date && this.date.getUTCFullYear();
17610 var currentMonth = this.date && this.date.getUTCMonth();
17612 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17614 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17615 v.removeClass('active');
17617 if(currentYear === year && k === currentMonth){
17618 v.addClass('active');
17621 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17622 v.addClass('disabled');
17628 year = parseInt(year/10, 10) * 10;
17630 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17632 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17635 for (var i = -1; i < 11; i++) {
17636 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17638 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17646 showMode: function(dir)
17649 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17652 Roo.each(this.picker().select('>div',true).elements, function(v){
17653 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17656 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17661 if(this.isInline) {
17665 this.picker().removeClass(['bottom', 'top']);
17667 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17669 * place to the top of element!
17673 this.picker().addClass('top');
17674 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17679 this.picker().addClass('bottom');
17681 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17684 parseDate : function(value)
17686 if(!value || value instanceof Date){
17689 var v = Date.parseDate(value, this.format);
17690 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17691 v = Date.parseDate(value, 'Y-m-d');
17693 if(!v && this.altFormats){
17694 if(!this.altFormatsArray){
17695 this.altFormatsArray = this.altFormats.split("|");
17697 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17698 v = Date.parseDate(value, this.altFormatsArray[i]);
17704 formatDate : function(date, fmt)
17706 return (!date || !(date instanceof Date)) ?
17707 date : date.dateFormat(fmt || this.format);
17710 onFocus : function()
17712 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17716 onBlur : function()
17718 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17720 var d = this.inputEl().getValue();
17729 this.picker().show();
17733 this.fireEvent('show', this, this.date);
17738 if(this.isInline) {
17741 this.picker().hide();
17742 this.viewMode = this.startViewMode;
17745 this.fireEvent('hide', this, this.date);
17749 onMousedown: function(e)
17751 e.stopPropagation();
17752 e.preventDefault();
17757 Roo.bootstrap.DateField.superclass.keyup.call(this);
17761 setValue: function(v)
17763 if(this.fireEvent('beforeselect', this, v) !== false){
17764 var d = new Date(this.parseDate(v) ).clearTime();
17766 if(isNaN(d.getTime())){
17767 this.date = this.viewDate = '';
17768 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17772 v = this.formatDate(d);
17774 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17776 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17780 this.fireEvent('select', this, this.date);
17784 getValue: function()
17786 return this.formatDate(this.date);
17789 fireKey: function(e)
17791 if (!this.picker().isVisible()){
17792 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17798 var dateChanged = false,
17800 newDate, newViewDate;
17805 e.preventDefault();
17809 if (!this.keyboardNavigation) {
17812 dir = e.keyCode == 37 ? -1 : 1;
17815 newDate = this.moveYear(this.date, dir);
17816 newViewDate = this.moveYear(this.viewDate, dir);
17817 } else if (e.shiftKey){
17818 newDate = this.moveMonth(this.date, dir);
17819 newViewDate = this.moveMonth(this.viewDate, dir);
17821 newDate = new Date(this.date);
17822 newDate.setUTCDate(this.date.getUTCDate() + dir);
17823 newViewDate = new Date(this.viewDate);
17824 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17826 if (this.dateWithinRange(newDate)){
17827 this.date = newDate;
17828 this.viewDate = newViewDate;
17829 this.setValue(this.formatDate(this.date));
17831 e.preventDefault();
17832 dateChanged = true;
17837 if (!this.keyboardNavigation) {
17840 dir = e.keyCode == 38 ? -1 : 1;
17842 newDate = this.moveYear(this.date, dir);
17843 newViewDate = this.moveYear(this.viewDate, dir);
17844 } else if (e.shiftKey){
17845 newDate = this.moveMonth(this.date, dir);
17846 newViewDate = this.moveMonth(this.viewDate, dir);
17848 newDate = new Date(this.date);
17849 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17850 newViewDate = new Date(this.viewDate);
17851 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17853 if (this.dateWithinRange(newDate)){
17854 this.date = newDate;
17855 this.viewDate = newViewDate;
17856 this.setValue(this.formatDate(this.date));
17858 e.preventDefault();
17859 dateChanged = true;
17863 this.setValue(this.formatDate(this.date));
17865 e.preventDefault();
17868 this.setValue(this.formatDate(this.date));
17882 onClick: function(e)
17884 e.stopPropagation();
17885 e.preventDefault();
17887 var target = e.getTarget();
17889 if(target.nodeName.toLowerCase() === 'i'){
17890 target = Roo.get(target).dom.parentNode;
17893 var nodeName = target.nodeName;
17894 var className = target.className;
17895 var html = target.innerHTML;
17896 //Roo.log(nodeName);
17898 switch(nodeName.toLowerCase()) {
17900 switch(className) {
17906 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17907 switch(this.viewMode){
17909 this.viewDate = this.moveMonth(this.viewDate, dir);
17913 this.viewDate = this.moveYear(this.viewDate, dir);
17919 var date = new Date();
17920 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17922 this.setValue(this.formatDate(this.date));
17929 if (className.indexOf('disabled') < 0) {
17930 this.viewDate.setUTCDate(1);
17931 if (className.indexOf('month') > -1) {
17932 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17934 var year = parseInt(html, 10) || 0;
17935 this.viewDate.setUTCFullYear(year);
17939 if(this.singleMode){
17940 this.setValue(this.formatDate(this.viewDate));
17951 //Roo.log(className);
17952 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17953 var day = parseInt(html, 10) || 1;
17954 var year = this.viewDate.getUTCFullYear(),
17955 month = this.viewDate.getUTCMonth();
17957 if (className.indexOf('old') > -1) {
17964 } else if (className.indexOf('new') > -1) {
17972 //Roo.log([year,month,day]);
17973 this.date = this.UTCDate(year, month, day,0,0,0,0);
17974 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17976 //Roo.log(this.formatDate(this.date));
17977 this.setValue(this.formatDate(this.date));
17984 setStartDate: function(startDate)
17986 this.startDate = startDate || -Infinity;
17987 if (this.startDate !== -Infinity) {
17988 this.startDate = this.parseDate(this.startDate);
17991 this.updateNavArrows();
17994 setEndDate: function(endDate)
17996 this.endDate = endDate || Infinity;
17997 if (this.endDate !== Infinity) {
17998 this.endDate = this.parseDate(this.endDate);
18001 this.updateNavArrows();
18004 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18006 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18007 if (typeof(this.daysOfWeekDisabled) !== 'object') {
18008 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18010 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18011 return parseInt(d, 10);
18014 this.updateNavArrows();
18017 updateNavArrows: function()
18019 if(this.singleMode){
18023 var d = new Date(this.viewDate),
18024 year = d.getUTCFullYear(),
18025 month = d.getUTCMonth();
18027 Roo.each(this.picker().select('.prev', true).elements, function(v){
18029 switch (this.viewMode) {
18032 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18038 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18045 Roo.each(this.picker().select('.next', true).elements, function(v){
18047 switch (this.viewMode) {
18050 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18056 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18064 moveMonth: function(date, dir)
18069 var new_date = new Date(date.valueOf()),
18070 day = new_date.getUTCDate(),
18071 month = new_date.getUTCMonth(),
18072 mag = Math.abs(dir),
18074 dir = dir > 0 ? 1 : -1;
18077 // If going back one month, make sure month is not current month
18078 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18080 return new_date.getUTCMonth() == month;
18082 // If going forward one month, make sure month is as expected
18083 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18085 return new_date.getUTCMonth() != new_month;
18087 new_month = month + dir;
18088 new_date.setUTCMonth(new_month);
18089 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18090 if (new_month < 0 || new_month > 11) {
18091 new_month = (new_month + 12) % 12;
18094 // For magnitudes >1, move one month at a time...
18095 for (var i=0; i<mag; i++) {
18096 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18097 new_date = this.moveMonth(new_date, dir);
18099 // ...then reset the day, keeping it in the new month
18100 new_month = new_date.getUTCMonth();
18101 new_date.setUTCDate(day);
18103 return new_month != new_date.getUTCMonth();
18106 // Common date-resetting loop -- if date is beyond end of month, make it
18109 new_date.setUTCDate(--day);
18110 new_date.setUTCMonth(new_month);
18115 moveYear: function(date, dir)
18117 return this.moveMonth(date, dir*12);
18120 dateWithinRange: function(date)
18122 return date >= this.startDate && date <= this.endDate;
18128 this.picker().remove();
18131 validateValue : function(value)
18133 if(value.length < 1) {
18134 if(this.allowBlank){
18140 if(value.length < this.minLength){
18143 if(value.length > this.maxLength){
18147 var vt = Roo.form.VTypes;
18148 if(!vt[this.vtype](value, this)){
18152 if(typeof this.validator == "function"){
18153 var msg = this.validator(value);
18159 if(this.regex && !this.regex.test(value)){
18163 if(typeof(this.parseDate(value)) == 'undefined'){
18167 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18171 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18181 Roo.apply(Roo.bootstrap.DateField, {
18192 html: '<i class="fa fa-arrow-left"/>'
18202 html: '<i class="fa fa-arrow-right"/>'
18244 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18245 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18246 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18247 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18248 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18261 navFnc: 'FullYear',
18266 navFnc: 'FullYear',
18271 Roo.apply(Roo.bootstrap.DateField, {
18275 cls: 'datepicker dropdown-menu roo-dynamic',
18279 cls: 'datepicker-days',
18283 cls: 'table-condensed',
18285 Roo.bootstrap.DateField.head,
18289 Roo.bootstrap.DateField.footer
18296 cls: 'datepicker-months',
18300 cls: 'table-condensed',
18302 Roo.bootstrap.DateField.head,
18303 Roo.bootstrap.DateField.content,
18304 Roo.bootstrap.DateField.footer
18311 cls: 'datepicker-years',
18315 cls: 'table-condensed',
18317 Roo.bootstrap.DateField.head,
18318 Roo.bootstrap.DateField.content,
18319 Roo.bootstrap.DateField.footer
18338 * @class Roo.bootstrap.TimeField
18339 * @extends Roo.bootstrap.Input
18340 * Bootstrap DateField class
18344 * Create a new TimeField
18345 * @param {Object} config The config object
18348 Roo.bootstrap.TimeField = function(config){
18349 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18353 * Fires when this field show.
18354 * @param {Roo.bootstrap.DateField} thisthis
18355 * @param {Mixed} date The date value
18360 * Fires when this field hide.
18361 * @param {Roo.bootstrap.DateField} this
18362 * @param {Mixed} date The date value
18367 * Fires when select a date.
18368 * @param {Roo.bootstrap.DateField} this
18369 * @param {Mixed} date The date value
18375 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18378 * @cfg {String} format
18379 * The default time format string which can be overriden for localization support. The format must be
18380 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18384 onRender: function(ct, position)
18387 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18389 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18391 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18393 this.pop = this.picker().select('>.datepicker-time',true).first();
18394 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18396 this.picker().on('mousedown', this.onMousedown, this);
18397 this.picker().on('click', this.onClick, this);
18399 this.picker().addClass('datepicker-dropdown');
18404 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18405 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18406 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18407 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18408 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18409 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18413 fireKey: function(e){
18414 if (!this.picker().isVisible()){
18415 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18421 e.preventDefault();
18429 this.onTogglePeriod();
18432 this.onIncrementMinutes();
18435 this.onDecrementMinutes();
18444 onClick: function(e) {
18445 e.stopPropagation();
18446 e.preventDefault();
18449 picker : function()
18451 return this.el.select('.datepicker', true).first();
18454 fillTime: function()
18456 var time = this.pop.select('tbody', true).first();
18458 time.dom.innerHTML = '';
18473 cls: 'hours-up glyphicon glyphicon-chevron-up'
18493 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18514 cls: 'timepicker-hour',
18529 cls: 'timepicker-minute',
18544 cls: 'btn btn-primary period',
18566 cls: 'hours-down glyphicon glyphicon-chevron-down'
18586 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18604 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18611 var hours = this.time.getHours();
18612 var minutes = this.time.getMinutes();
18625 hours = hours - 12;
18629 hours = '0' + hours;
18633 minutes = '0' + minutes;
18636 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18637 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18638 this.pop.select('button', true).first().dom.innerHTML = period;
18644 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18646 var cls = ['bottom'];
18648 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18655 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18660 this.picker().addClass(cls.join('-'));
18664 Roo.each(cls, function(c){
18666 _this.picker().setTop(_this.inputEl().getHeight());
18670 _this.picker().setTop(0 - _this.picker().getHeight());
18675 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18679 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18686 onFocus : function()
18688 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18692 onBlur : function()
18694 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18700 this.picker().show();
18705 this.fireEvent('show', this, this.date);
18710 this.picker().hide();
18713 this.fireEvent('hide', this, this.date);
18716 setTime : function()
18719 this.setValue(this.time.format(this.format));
18721 this.fireEvent('select', this, this.date);
18726 onMousedown: function(e){
18727 e.stopPropagation();
18728 e.preventDefault();
18731 onIncrementHours: function()
18733 Roo.log('onIncrementHours');
18734 this.time = this.time.add(Date.HOUR, 1);
18739 onDecrementHours: function()
18741 Roo.log('onDecrementHours');
18742 this.time = this.time.add(Date.HOUR, -1);
18746 onIncrementMinutes: function()
18748 Roo.log('onIncrementMinutes');
18749 this.time = this.time.add(Date.MINUTE, 1);
18753 onDecrementMinutes: function()
18755 Roo.log('onDecrementMinutes');
18756 this.time = this.time.add(Date.MINUTE, -1);
18760 onTogglePeriod: function()
18762 Roo.log('onTogglePeriod');
18763 this.time = this.time.add(Date.HOUR, 12);
18770 Roo.apply(Roo.bootstrap.TimeField, {
18800 cls: 'btn btn-info ok',
18812 Roo.apply(Roo.bootstrap.TimeField, {
18816 cls: 'datepicker dropdown-menu',
18820 cls: 'datepicker-time',
18824 cls: 'table-condensed',
18826 Roo.bootstrap.TimeField.content,
18827 Roo.bootstrap.TimeField.footer
18846 * @class Roo.bootstrap.MonthField
18847 * @extends Roo.bootstrap.Input
18848 * Bootstrap MonthField class
18850 * @cfg {String} language default en
18853 * Create a new MonthField
18854 * @param {Object} config The config object
18857 Roo.bootstrap.MonthField = function(config){
18858 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18863 * Fires when this field show.
18864 * @param {Roo.bootstrap.MonthField} this
18865 * @param {Mixed} date The date value
18870 * Fires when this field hide.
18871 * @param {Roo.bootstrap.MonthField} this
18872 * @param {Mixed} date The date value
18877 * Fires when select a date.
18878 * @param {Roo.bootstrap.MonthField} this
18879 * @param {String} oldvalue The old value
18880 * @param {String} newvalue The new value
18886 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18888 onRender: function(ct, position)
18891 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18893 this.language = this.language || 'en';
18894 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18895 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18897 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18898 this.isInline = false;
18899 this.isInput = true;
18900 this.component = this.el.select('.add-on', true).first() || false;
18901 this.component = (this.component && this.component.length === 0) ? false : this.component;
18902 this.hasInput = this.component && this.inputEL().length;
18904 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18906 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18908 this.picker().on('mousedown', this.onMousedown, this);
18909 this.picker().on('click', this.onClick, this);
18911 this.picker().addClass('datepicker-dropdown');
18913 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18914 v.setStyle('width', '189px');
18921 if(this.isInline) {
18927 setValue: function(v, suppressEvent)
18929 var o = this.getValue();
18931 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18935 if(suppressEvent !== true){
18936 this.fireEvent('select', this, o, v);
18941 getValue: function()
18946 onClick: function(e)
18948 e.stopPropagation();
18949 e.preventDefault();
18951 var target = e.getTarget();
18953 if(target.nodeName.toLowerCase() === 'i'){
18954 target = Roo.get(target).dom.parentNode;
18957 var nodeName = target.nodeName;
18958 var className = target.className;
18959 var html = target.innerHTML;
18961 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18965 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18967 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18973 picker : function()
18975 return this.pickerEl;
18978 fillMonths: function()
18981 var months = this.picker().select('>.datepicker-months td', true).first();
18983 months.dom.innerHTML = '';
18989 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18992 months.createChild(month);
19001 if(typeof(this.vIndex) == 'undefined' && this.value.length){
19002 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19005 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19006 e.removeClass('active');
19008 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19009 e.addClass('active');
19016 if(this.isInline) {
19020 this.picker().removeClass(['bottom', 'top']);
19022 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19024 * place to the top of element!
19028 this.picker().addClass('top');
19029 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19034 this.picker().addClass('bottom');
19036 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19039 onFocus : function()
19041 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19045 onBlur : function()
19047 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19049 var d = this.inputEl().getValue();
19058 this.picker().show();
19059 this.picker().select('>.datepicker-months', true).first().show();
19063 this.fireEvent('show', this, this.date);
19068 if(this.isInline) {
19071 this.picker().hide();
19072 this.fireEvent('hide', this, this.date);
19076 onMousedown: function(e)
19078 e.stopPropagation();
19079 e.preventDefault();
19084 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19088 fireKey: function(e)
19090 if (!this.picker().isVisible()){
19091 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19102 e.preventDefault();
19106 dir = e.keyCode == 37 ? -1 : 1;
19108 this.vIndex = this.vIndex + dir;
19110 if(this.vIndex < 0){
19114 if(this.vIndex > 11){
19118 if(isNaN(this.vIndex)){
19122 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19128 dir = e.keyCode == 38 ? -1 : 1;
19130 this.vIndex = this.vIndex + dir * 4;
19132 if(this.vIndex < 0){
19136 if(this.vIndex > 11){
19140 if(isNaN(this.vIndex)){
19144 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19149 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19150 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19154 e.preventDefault();
19157 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19158 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19174 this.picker().remove();
19179 Roo.apply(Roo.bootstrap.MonthField, {
19198 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19199 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19204 Roo.apply(Roo.bootstrap.MonthField, {
19208 cls: 'datepicker dropdown-menu roo-dynamic',
19212 cls: 'datepicker-months',
19216 cls: 'table-condensed',
19218 Roo.bootstrap.DateField.content
19238 * @class Roo.bootstrap.CheckBox
19239 * @extends Roo.bootstrap.Input
19240 * Bootstrap CheckBox class
19242 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19243 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19244 * @cfg {String} boxLabel The text that appears beside the checkbox
19245 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19246 * @cfg {Boolean} checked initnal the element
19247 * @cfg {Boolean} inline inline the element (default false)
19248 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19251 * Create a new CheckBox
19252 * @param {Object} config The config object
19255 Roo.bootstrap.CheckBox = function(config){
19256 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19261 * Fires when the element is checked or unchecked.
19262 * @param {Roo.bootstrap.CheckBox} this This input
19263 * @param {Boolean} checked The new checked value
19270 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
19272 inputType: 'checkbox',
19280 getAutoCreate : function()
19282 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19288 cfg.cls = 'form-group ' + this.inputType; //input-group
19291 cfg.cls += ' ' + this.inputType + '-inline';
19297 type : this.inputType,
19298 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
19299 cls : 'roo-' + this.inputType, //'form-box',
19300 placeholder : this.placeholder || ''
19304 if (this.weight) { // Validity check?
19305 cfg.cls += " " + this.inputType + "-" + this.weight;
19308 if (this.disabled) {
19309 input.disabled=true;
19313 input.checked = this.checked;
19317 input.name = this.name;
19321 input.cls += ' input-' + this.size;
19326 ['xs','sm','md','lg'].map(function(size){
19327 if (settings[size]) {
19328 cfg.cls += ' col-' + size + '-' + settings[size];
19332 var inputblock = input;
19334 if (this.before || this.after) {
19337 cls : 'input-group',
19342 inputblock.cn.push({
19344 cls : 'input-group-addon',
19349 inputblock.cn.push(input);
19352 inputblock.cn.push({
19354 cls : 'input-group-addon',
19361 if (align ==='left' && this.fieldLabel.length) {
19362 // Roo.log("left and has label");
19368 cls : 'control-label col-md-' + this.labelWidth,
19369 html : this.fieldLabel
19373 cls : "col-md-" + (12 - this.labelWidth),
19380 } else if ( this.fieldLabel.length) {
19381 // Roo.log(" label");
19385 tag: this.boxLabel ? 'span' : 'label',
19387 cls: 'control-label box-input-label',
19388 //cls : 'input-group-addon',
19389 html : this.fieldLabel
19399 // Roo.log(" no label && no align");
19400 cfg.cn = [ inputblock ] ;
19406 var boxLabelCfg = {
19408 //'for': id, // box label is handled by onclick - so no for...
19410 html: this.boxLabel
19414 boxLabelCfg.tooltip = this.tooltip;
19417 cfg.cn.push(boxLabelCfg);
19427 * return the real input element.
19429 inputEl: function ()
19431 return this.el.select('input.roo-' + this.inputType,true).first();
19434 labelEl: function()
19436 return this.el.select('label.control-label',true).first();
19438 /* depricated... */
19442 return this.labelEl();
19445 boxLabelEl: function()
19447 return this.el.select('label.box-label',true).first();
19450 initEvents : function()
19452 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19454 this.inputEl().on('click', this.onClick, this);
19456 if (this.boxLabel) {
19457 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19460 this.startValue = this.getValue();
19463 Roo.bootstrap.CheckBox.register(this);
19467 onClick : function()
19469 this.setChecked(!this.checked);
19472 setChecked : function(state,suppressEvent)
19474 this.startValue = this.getValue();
19476 if(this.inputType == 'radio'){
19478 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19479 e.dom.checked = false;
19482 this.inputEl().dom.checked = true;
19484 this.inputEl().dom.value = this.inputValue;
19486 if(suppressEvent !== true){
19487 this.fireEvent('check', this, true);
19495 this.checked = state;
19497 this.inputEl().dom.checked = state;
19499 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19501 if(suppressEvent !== true){
19502 this.fireEvent('check', this, state);
19508 getValue : function()
19510 if(this.inputType == 'radio'){
19511 return this.getGroupValue();
19514 return this.inputEl().getValue();
19518 getGroupValue : function()
19520 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19524 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19527 setValue : function(v,suppressEvent)
19529 if(this.inputType == 'radio'){
19530 this.setGroupValue(v, suppressEvent);
19534 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19539 setGroupValue : function(v, suppressEvent)
19541 this.startValue = this.getValue();
19543 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19544 e.dom.checked = false;
19546 if(e.dom.value == v){
19547 e.dom.checked = true;
19551 if(suppressEvent !== true){
19552 this.fireEvent('check', this, true);
19560 validate : function()
19564 (this.inputType == 'radio' && this.validateRadio()) ||
19565 (this.inputType == 'checkbox' && this.validateCheckbox())
19571 this.markInvalid();
19575 validateRadio : function()
19577 if(this.allowBlank){
19583 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19584 if(!e.dom.checked){
19596 validateCheckbox : function()
19599 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19602 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19610 for(var i in group){
19615 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19622 * Mark this field as valid
19624 markValid : function()
19626 if(this.allowBlank){
19632 this.fireEvent('valid', this);
19634 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19637 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19644 if(this.inputType == 'radio'){
19645 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19646 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19647 e.findParent('.form-group', false, true).addClass(_this.validClass);
19654 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19655 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19659 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19665 for(var i in group){
19666 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19667 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19672 * Mark this field as invalid
19673 * @param {String} msg The validation message
19675 markInvalid : function(msg)
19677 if(this.allowBlank){
19683 this.fireEvent('invalid', this, msg);
19685 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19688 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19692 label.markInvalid();
19695 if(this.inputType == 'radio'){
19696 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19697 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19698 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19705 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19706 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19710 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19716 for(var i in group){
19717 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19718 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19723 disable : function()
19725 if(this.inputType != 'radio'){
19726 Roo.bootstrap.CheckBox.superclass.disable.call(this);
19733 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19734 _this.getActionEl().addClass(this.disabledClass);
19735 e.dom.disabled = true;
19739 this.disabled = true;
19740 this.fireEvent("disable", this);
19744 enable : function()
19746 if(this.inputType != 'radio'){
19747 Roo.bootstrap.CheckBox.superclass.enable.call(this);
19754 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19755 _this.getActionEl().removeClass(this.disabledClass);
19756 e.dom.disabled = false;
19760 this.disabled = false;
19761 this.fireEvent("enable", this);
19768 Roo.apply(Roo.bootstrap.CheckBox, {
19773 * register a CheckBox Group
19774 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19776 register : function(checkbox)
19778 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19779 this.groups[checkbox.groupId] = {};
19782 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19786 this.groups[checkbox.groupId][checkbox.name] = checkbox;
19790 * fetch a CheckBox Group based on the group ID
19791 * @param {string} the group ID
19792 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19794 get: function(groupId) {
19795 if (typeof(this.groups[groupId]) == 'undefined') {
19799 return this.groups[groupId] ;
19811 *<div class="radio">
19813 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19814 Option one is this and that—be sure to include why it's great
19821 *<label class="radio-inline">fieldLabel</label>
19822 *<label class="radio-inline">
19823 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19831 * @class Roo.bootstrap.Radio
19832 * @extends Roo.bootstrap.CheckBox
19833 * Bootstrap Radio class
19836 * Create a new Radio
19837 * @param {Object} config The config object
19840 Roo.bootstrap.Radio = function(config){
19841 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19845 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19847 inputType: 'radio',
19851 getAutoCreate : function()
19853 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19854 align = align || 'left'; // default...
19861 tag : this.inline ? 'span' : 'div',
19866 var inline = this.inline ? ' radio-inline' : '';
19870 // does not need for, as we wrap the input with it..
19872 cls : 'control-label box-label' + inline,
19875 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19879 //cls : 'control-label' + inline,
19880 html : this.fieldLabel,
19881 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19890 type : this.inputType,
19891 //value : (!this.checked) ? this.valueOff : this.inputValue,
19892 value : this.inputValue,
19894 placeholder : this.placeholder || '' // ?? needed????
19897 if (this.weight) { // Validity check?
19898 input.cls += " radio-" + this.weight;
19900 if (this.disabled) {
19901 input.disabled=true;
19905 input.checked = this.checked;
19909 input.name = this.name;
19913 input.cls += ' input-' + this.size;
19916 //?? can span's inline have a width??
19919 ['xs','sm','md','lg'].map(function(size){
19920 if (settings[size]) {
19921 cfg.cls += ' col-' + size + '-' + settings[size];
19925 var inputblock = input;
19927 if (this.before || this.after) {
19930 cls : 'input-group',
19935 inputblock.cn.push({
19937 cls : 'input-group-addon',
19941 inputblock.cn.push(input);
19943 inputblock.cn.push({
19945 cls : 'input-group-addon',
19953 if (this.fieldLabel && this.fieldLabel.length) {
19954 cfg.cn.push(fieldLabel);
19957 // normal bootstrap puts the input inside the label.
19958 // however with our styled version - it has to go after the input.
19960 //lbl.cn.push(inputblock);
19964 cls: 'radio' + inline,
19971 cfg.cn.push( lblwrap);
19976 html: this.boxLabel
19985 initEvents : function()
19987 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19989 this.inputEl().on('click', this.onClick, this);
19990 if (this.boxLabel) {
19991 //Roo.log('find label');
19992 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19997 inputEl: function ()
19999 return this.el.select('input.roo-radio',true).first();
20001 onClick : function()
20004 this.setChecked(true);
20007 setChecked : function(state,suppressEvent)
20010 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20011 v.dom.checked = false;
20014 Roo.log(this.inputEl().dom);
20015 this.checked = state;
20016 this.inputEl().dom.checked = state;
20018 if(suppressEvent !== true){
20019 this.fireEvent('check', this, state);
20022 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20026 getGroupValue : function()
20029 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20030 if(v.dom.checked == true){
20031 value = v.dom.value;
20039 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
20040 * @return {Mixed} value The field value
20042 getValue : function(){
20043 return this.getGroupValue();
20049 //<script type="text/javascript">
20052 * Based Ext JS Library 1.1.1
20053 * Copyright(c) 2006-2007, Ext JS, LLC.
20059 * @class Roo.HtmlEditorCore
20060 * @extends Roo.Component
20061 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20063 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20066 Roo.HtmlEditorCore = function(config){
20069 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20074 * @event initialize
20075 * Fires when the editor is fully initialized (including the iframe)
20076 * @param {Roo.HtmlEditorCore} this
20081 * Fires when the editor is first receives the focus. Any insertion must wait
20082 * until after this event.
20083 * @param {Roo.HtmlEditorCore} this
20087 * @event beforesync
20088 * Fires before the textarea is updated with content from the editor iframe. Return false
20089 * to cancel the sync.
20090 * @param {Roo.HtmlEditorCore} this
20091 * @param {String} html
20095 * @event beforepush
20096 * Fires before the iframe editor is updated with content from the textarea. Return false
20097 * to cancel the push.
20098 * @param {Roo.HtmlEditorCore} this
20099 * @param {String} html
20104 * Fires when the textarea is updated with content from the editor iframe.
20105 * @param {Roo.HtmlEditorCore} this
20106 * @param {String} html
20111 * Fires when the iframe editor is updated with content from the textarea.
20112 * @param {Roo.HtmlEditorCore} this
20113 * @param {String} html
20118 * @event editorevent
20119 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20120 * @param {Roo.HtmlEditorCore} this
20126 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20128 // defaults : white / black...
20129 this.applyBlacklists();
20136 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20140 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20146 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20151 * @cfg {Number} height (in pixels)
20155 * @cfg {Number} width (in pixels)
20160 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20163 stylesheets: false,
20168 // private properties
20169 validationEvent : false,
20171 initialized : false,
20173 sourceEditMode : false,
20174 onFocus : Roo.emptyFn,
20176 hideMode:'offsets',
20180 // blacklist + whitelisted elements..
20187 * Protected method that will not generally be called directly. It
20188 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20189 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20191 getDocMarkup : function(){
20195 // inherit styels from page...??
20196 if (this.stylesheets === false) {
20198 Roo.get(document.head).select('style').each(function(node) {
20199 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20202 Roo.get(document.head).select('link').each(function(node) {
20203 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20206 } else if (!this.stylesheets.length) {
20208 st = '<style type="text/css">' +
20209 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20215 st += '<style type="text/css">' +
20216 'IMG { cursor: pointer } ' +
20220 return '<html><head>' + st +
20221 //<style type="text/css">' +
20222 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20224 ' </head><body class="roo-htmleditor-body"></body></html>';
20228 onRender : function(ct, position)
20231 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20232 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20235 this.el.dom.style.border = '0 none';
20236 this.el.dom.setAttribute('tabIndex', -1);
20237 this.el.addClass('x-hidden hide');
20241 if(Roo.isIE){ // fix IE 1px bogus margin
20242 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20246 this.frameId = Roo.id();
20250 var iframe = this.owner.wrap.createChild({
20252 cls: 'form-control', // bootstrap..
20254 name: this.frameId,
20255 frameBorder : 'no',
20256 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20261 this.iframe = iframe.dom;
20263 this.assignDocWin();
20265 this.doc.designMode = 'on';
20268 this.doc.write(this.getDocMarkup());
20272 var task = { // must defer to wait for browser to be ready
20274 //console.log("run task?" + this.doc.readyState);
20275 this.assignDocWin();
20276 if(this.doc.body || this.doc.readyState == 'complete'){
20278 this.doc.designMode="on";
20282 Roo.TaskMgr.stop(task);
20283 this.initEditor.defer(10, this);
20290 Roo.TaskMgr.start(task);
20295 onResize : function(w, h)
20297 Roo.log('resize: ' +w + ',' + h );
20298 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20302 if(typeof w == 'number'){
20304 this.iframe.style.width = w + 'px';
20306 if(typeof h == 'number'){
20308 this.iframe.style.height = h + 'px';
20310 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20317 * Toggles the editor between standard and source edit mode.
20318 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20320 toggleSourceEdit : function(sourceEditMode){
20322 this.sourceEditMode = sourceEditMode === true;
20324 if(this.sourceEditMode){
20326 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20329 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20330 //this.iframe.className = '';
20333 //this.setSize(this.owner.wrap.getSize());
20334 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20341 * Protected method that will not generally be called directly. If you need/want
20342 * custom HTML cleanup, this is the method you should override.
20343 * @param {String} html The HTML to be cleaned
20344 * return {String} The cleaned HTML
20346 cleanHtml : function(html){
20347 html = String(html);
20348 if(html.length > 5){
20349 if(Roo.isSafari){ // strip safari nonsense
20350 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20353 if(html == ' '){
20360 * HTML Editor -> Textarea
20361 * Protected method that will not generally be called directly. Syncs the contents
20362 * of the editor iframe with the textarea.
20364 syncValue : function(){
20365 if(this.initialized){
20366 var bd = (this.doc.body || this.doc.documentElement);
20367 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20368 var html = bd.innerHTML;
20370 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20371 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20373 html = '<div style="'+m[0]+'">' + html + '</div>';
20376 html = this.cleanHtml(html);
20377 // fix up the special chars.. normaly like back quotes in word...
20378 // however we do not want to do this with chinese..
20379 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20380 var cc = b.charCodeAt();
20382 (cc >= 0x4E00 && cc < 0xA000 ) ||
20383 (cc >= 0x3400 && cc < 0x4E00 ) ||
20384 (cc >= 0xf900 && cc < 0xfb00 )
20390 if(this.owner.fireEvent('beforesync', this, html) !== false){
20391 this.el.dom.value = html;
20392 this.owner.fireEvent('sync', this, html);
20398 * Protected method that will not generally be called directly. Pushes the value of the textarea
20399 * into the iframe editor.
20401 pushValue : function(){
20402 if(this.initialized){
20403 var v = this.el.dom.value.trim();
20405 // if(v.length < 1){
20409 if(this.owner.fireEvent('beforepush', this, v) !== false){
20410 var d = (this.doc.body || this.doc.documentElement);
20412 this.cleanUpPaste();
20413 this.el.dom.value = d.innerHTML;
20414 this.owner.fireEvent('push', this, v);
20420 deferFocus : function(){
20421 this.focus.defer(10, this);
20425 focus : function(){
20426 if(this.win && !this.sourceEditMode){
20433 assignDocWin: function()
20435 var iframe = this.iframe;
20438 this.doc = iframe.contentWindow.document;
20439 this.win = iframe.contentWindow;
20441 // if (!Roo.get(this.frameId)) {
20444 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20445 // this.win = Roo.get(this.frameId).dom.contentWindow;
20447 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20451 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20452 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20457 initEditor : function(){
20458 //console.log("INIT EDITOR");
20459 this.assignDocWin();
20463 this.doc.designMode="on";
20465 this.doc.write(this.getDocMarkup());
20468 var dbody = (this.doc.body || this.doc.documentElement);
20469 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20470 // this copies styles from the containing element into thsi one..
20471 // not sure why we need all of this..
20472 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20474 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20475 //ss['background-attachment'] = 'fixed'; // w3c
20476 dbody.bgProperties = 'fixed'; // ie
20477 //Roo.DomHelper.applyStyles(dbody, ss);
20478 Roo.EventManager.on(this.doc, {
20479 //'mousedown': this.onEditorEvent,
20480 'mouseup': this.onEditorEvent,
20481 'dblclick': this.onEditorEvent,
20482 'click': this.onEditorEvent,
20483 'keyup': this.onEditorEvent,
20488 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20490 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20491 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20493 this.initialized = true;
20495 this.owner.fireEvent('initialize', this);
20500 onDestroy : function(){
20506 //for (var i =0; i < this.toolbars.length;i++) {
20507 // // fixme - ask toolbars for heights?
20508 // this.toolbars[i].onDestroy();
20511 //this.wrap.dom.innerHTML = '';
20512 //this.wrap.remove();
20517 onFirstFocus : function(){
20519 this.assignDocWin();
20522 this.activated = true;
20525 if(Roo.isGecko){ // prevent silly gecko errors
20527 var s = this.win.getSelection();
20528 if(!s.focusNode || s.focusNode.nodeType != 3){
20529 var r = s.getRangeAt(0);
20530 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20535 this.execCmd('useCSS', true);
20536 this.execCmd('styleWithCSS', false);
20539 this.owner.fireEvent('activate', this);
20543 adjustFont: function(btn){
20544 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20545 //if(Roo.isSafari){ // safari
20548 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20549 if(Roo.isSafari){ // safari
20550 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20551 v = (v < 10) ? 10 : v;
20552 v = (v > 48) ? 48 : v;
20553 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20558 v = Math.max(1, v+adjust);
20560 this.execCmd('FontSize', v );
20563 onEditorEvent : function(e)
20565 this.owner.fireEvent('editorevent', this, e);
20566 // this.updateToolbar();
20567 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20570 insertTag : function(tg)
20572 // could be a bit smarter... -> wrap the current selected tRoo..
20573 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20575 range = this.createRange(this.getSelection());
20576 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20577 wrappingNode.appendChild(range.extractContents());
20578 range.insertNode(wrappingNode);
20585 this.execCmd("formatblock", tg);
20589 insertText : function(txt)
20593 var range = this.createRange();
20594 range.deleteContents();
20595 //alert(Sender.getAttribute('label'));
20597 range.insertNode(this.doc.createTextNode(txt));
20603 * Executes a Midas editor command on the editor document and performs necessary focus and
20604 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20605 * @param {String} cmd The Midas command
20606 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20608 relayCmd : function(cmd, value){
20610 this.execCmd(cmd, value);
20611 this.owner.fireEvent('editorevent', this);
20612 //this.updateToolbar();
20613 this.owner.deferFocus();
20617 * Executes a Midas editor command directly on the editor document.
20618 * For visual commands, you should use {@link #relayCmd} instead.
20619 * <b>This should only be called after the editor is initialized.</b>
20620 * @param {String} cmd The Midas command
20621 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20623 execCmd : function(cmd, value){
20624 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20631 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20633 * @param {String} text | dom node..
20635 insertAtCursor : function(text)
20640 if(!this.activated){
20646 var r = this.doc.selection.createRange();
20657 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20661 // from jquery ui (MIT licenced)
20663 var win = this.win;
20665 if (win.getSelection && win.getSelection().getRangeAt) {
20666 range = win.getSelection().getRangeAt(0);
20667 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20668 range.insertNode(node);
20669 } else if (win.document.selection && win.document.selection.createRange) {
20670 // no firefox support
20671 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20672 win.document.selection.createRange().pasteHTML(txt);
20674 // no firefox support
20675 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20676 this.execCmd('InsertHTML', txt);
20685 mozKeyPress : function(e){
20687 var c = e.getCharCode(), cmd;
20690 c = String.fromCharCode(c).toLowerCase();
20704 this.cleanUpPaste.defer(100, this);
20712 e.preventDefault();
20720 fixKeys : function(){ // load time branching for fastest keydown performance
20722 return function(e){
20723 var k = e.getKey(), r;
20726 r = this.doc.selection.createRange();
20729 r.pasteHTML('    ');
20736 r = this.doc.selection.createRange();
20738 var target = r.parentElement();
20739 if(!target || target.tagName.toLowerCase() != 'li'){
20741 r.pasteHTML('<br />');
20747 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20748 this.cleanUpPaste.defer(100, this);
20754 }else if(Roo.isOpera){
20755 return function(e){
20756 var k = e.getKey();
20760 this.execCmd('InsertHTML','    ');
20763 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20764 this.cleanUpPaste.defer(100, this);
20769 }else if(Roo.isSafari){
20770 return function(e){
20771 var k = e.getKey();
20775 this.execCmd('InsertText','\t');
20779 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20780 this.cleanUpPaste.defer(100, this);
20788 getAllAncestors: function()
20790 var p = this.getSelectedNode();
20793 a.push(p); // push blank onto stack..
20794 p = this.getParentElement();
20798 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20802 a.push(this.doc.body);
20806 lastSelNode : false,
20809 getSelection : function()
20811 this.assignDocWin();
20812 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20815 getSelectedNode: function()
20817 // this may only work on Gecko!!!
20819 // should we cache this!!!!
20824 var range = this.createRange(this.getSelection()).cloneRange();
20827 var parent = range.parentElement();
20829 var testRange = range.duplicate();
20830 testRange.moveToElementText(parent);
20831 if (testRange.inRange(range)) {
20834 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20837 parent = parent.parentElement;
20842 // is ancestor a text element.
20843 var ac = range.commonAncestorContainer;
20844 if (ac.nodeType == 3) {
20845 ac = ac.parentNode;
20848 var ar = ac.childNodes;
20851 var other_nodes = [];
20852 var has_other_nodes = false;
20853 for (var i=0;i<ar.length;i++) {
20854 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20857 // fullly contained node.
20859 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20864 // probably selected..
20865 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20866 other_nodes.push(ar[i]);
20870 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20875 has_other_nodes = true;
20877 if (!nodes.length && other_nodes.length) {
20878 nodes= other_nodes;
20880 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20886 createRange: function(sel)
20888 // this has strange effects when using with
20889 // top toolbar - not sure if it's a great idea.
20890 //this.editor.contentWindow.focus();
20891 if (typeof sel != "undefined") {
20893 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20895 return this.doc.createRange();
20898 return this.doc.createRange();
20901 getParentElement: function()
20904 this.assignDocWin();
20905 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20907 var range = this.createRange(sel);
20910 var p = range.commonAncestorContainer;
20911 while (p.nodeType == 3) { // text node
20922 * Range intersection.. the hard stuff...
20926 * [ -- selected range --- ]
20930 * if end is before start or hits it. fail.
20931 * if start is after end or hits it fail.
20933 * if either hits (but other is outside. - then it's not
20939 // @see http://www.thismuchiknow.co.uk/?p=64.
20940 rangeIntersectsNode : function(range, node)
20942 var nodeRange = node.ownerDocument.createRange();
20944 nodeRange.selectNode(node);
20946 nodeRange.selectNodeContents(node);
20949 var rangeStartRange = range.cloneRange();
20950 rangeStartRange.collapse(true);
20952 var rangeEndRange = range.cloneRange();
20953 rangeEndRange.collapse(false);
20955 var nodeStartRange = nodeRange.cloneRange();
20956 nodeStartRange.collapse(true);
20958 var nodeEndRange = nodeRange.cloneRange();
20959 nodeEndRange.collapse(false);
20961 return rangeStartRange.compareBoundaryPoints(
20962 Range.START_TO_START, nodeEndRange) == -1 &&
20963 rangeEndRange.compareBoundaryPoints(
20964 Range.START_TO_START, nodeStartRange) == 1;
20968 rangeCompareNode : function(range, node)
20970 var nodeRange = node.ownerDocument.createRange();
20972 nodeRange.selectNode(node);
20974 nodeRange.selectNodeContents(node);
20978 range.collapse(true);
20980 nodeRange.collapse(true);
20982 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20983 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20985 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20987 var nodeIsBefore = ss == 1;
20988 var nodeIsAfter = ee == -1;
20990 if (nodeIsBefore && nodeIsAfter) {
20993 if (!nodeIsBefore && nodeIsAfter) {
20994 return 1; //right trailed.
20997 if (nodeIsBefore && !nodeIsAfter) {
20998 return 2; // left trailed.
21004 // private? - in a new class?
21005 cleanUpPaste : function()
21007 // cleans up the whole document..
21008 Roo.log('cleanuppaste');
21010 this.cleanUpChildren(this.doc.body);
21011 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21012 if (clean != this.doc.body.innerHTML) {
21013 this.doc.body.innerHTML = clean;
21018 cleanWordChars : function(input) {// change the chars to hex code
21019 var he = Roo.HtmlEditorCore;
21021 var output = input;
21022 Roo.each(he.swapCodes, function(sw) {
21023 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21025 output = output.replace(swapper, sw[1]);
21032 cleanUpChildren : function (n)
21034 if (!n.childNodes.length) {
21037 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21038 this.cleanUpChild(n.childNodes[i]);
21045 cleanUpChild : function (node)
21048 //console.log(node);
21049 if (node.nodeName == "#text") {
21050 // clean up silly Windows -- stuff?
21053 if (node.nodeName == "#comment") {
21054 node.parentNode.removeChild(node);
21055 // clean up silly Windows -- stuff?
21058 var lcname = node.tagName.toLowerCase();
21059 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21060 // whitelist of tags..
21062 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21064 node.parentNode.removeChild(node);
21069 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21071 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21072 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21074 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21075 // remove_keep_children = true;
21078 if (remove_keep_children) {
21079 this.cleanUpChildren(node);
21080 // inserts everything just before this node...
21081 while (node.childNodes.length) {
21082 var cn = node.childNodes[0];
21083 node.removeChild(cn);
21084 node.parentNode.insertBefore(cn, node);
21086 node.parentNode.removeChild(node);
21090 if (!node.attributes || !node.attributes.length) {
21091 this.cleanUpChildren(node);
21095 function cleanAttr(n,v)
21098 if (v.match(/^\./) || v.match(/^\//)) {
21101 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21104 if (v.match(/^#/)) {
21107 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21108 node.removeAttribute(n);
21112 var cwhite = this.cwhite;
21113 var cblack = this.cblack;
21115 function cleanStyle(n,v)
21117 if (v.match(/expression/)) { //XSS?? should we even bother..
21118 node.removeAttribute(n);
21122 var parts = v.split(/;/);
21125 Roo.each(parts, function(p) {
21126 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21130 var l = p.split(':').shift().replace(/\s+/g,'');
21131 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21133 if ( cwhite.length && cblack.indexOf(l) > -1) {
21134 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21135 //node.removeAttribute(n);
21139 // only allow 'c whitelisted system attributes'
21140 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21141 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21142 //node.removeAttribute(n);
21152 if (clean.length) {
21153 node.setAttribute(n, clean.join(';'));
21155 node.removeAttribute(n);
21161 for (var i = node.attributes.length-1; i > -1 ; i--) {
21162 var a = node.attributes[i];
21165 if (a.name.toLowerCase().substr(0,2)=='on') {
21166 node.removeAttribute(a.name);
21169 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21170 node.removeAttribute(a.name);
21173 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21174 cleanAttr(a.name,a.value); // fixme..
21177 if (a.name == 'style') {
21178 cleanStyle(a.name,a.value);
21181 /// clean up MS crap..
21182 // tecnically this should be a list of valid class'es..
21185 if (a.name == 'class') {
21186 if (a.value.match(/^Mso/)) {
21187 node.className = '';
21190 if (a.value.match(/body/)) {
21191 node.className = '';
21202 this.cleanUpChildren(node);
21208 * Clean up MS wordisms...
21210 cleanWord : function(node)
21215 this.cleanWord(this.doc.body);
21218 if (node.nodeName == "#text") {
21219 // clean up silly Windows -- stuff?
21222 if (node.nodeName == "#comment") {
21223 node.parentNode.removeChild(node);
21224 // clean up silly Windows -- stuff?
21228 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21229 node.parentNode.removeChild(node);
21233 // remove - but keep children..
21234 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21235 while (node.childNodes.length) {
21236 var cn = node.childNodes[0];
21237 node.removeChild(cn);
21238 node.parentNode.insertBefore(cn, node);
21240 node.parentNode.removeChild(node);
21241 this.iterateChildren(node, this.cleanWord);
21245 if (node.className.length) {
21247 var cn = node.className.split(/\W+/);
21249 Roo.each(cn, function(cls) {
21250 if (cls.match(/Mso[a-zA-Z]+/)) {
21255 node.className = cna.length ? cna.join(' ') : '';
21257 node.removeAttribute("class");
21261 if (node.hasAttribute("lang")) {
21262 node.removeAttribute("lang");
21265 if (node.hasAttribute("style")) {
21267 var styles = node.getAttribute("style").split(";");
21269 Roo.each(styles, function(s) {
21270 if (!s.match(/:/)) {
21273 var kv = s.split(":");
21274 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21277 // what ever is left... we allow.
21280 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21281 if (!nstyle.length) {
21282 node.removeAttribute('style');
21285 this.iterateChildren(node, this.cleanWord);
21291 * iterateChildren of a Node, calling fn each time, using this as the scole..
21292 * @param {DomNode} node node to iterate children of.
21293 * @param {Function} fn method of this class to call on each item.
21295 iterateChildren : function(node, fn)
21297 if (!node.childNodes.length) {
21300 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21301 fn.call(this, node.childNodes[i])
21307 * cleanTableWidths.
21309 * Quite often pasting from word etc.. results in tables with column and widths.
21310 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21313 cleanTableWidths : function(node)
21318 this.cleanTableWidths(this.doc.body);
21323 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21326 Roo.log(node.tagName);
21327 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21328 this.iterateChildren(node, this.cleanTableWidths);
21331 if (node.hasAttribute('width')) {
21332 node.removeAttribute('width');
21336 if (node.hasAttribute("style")) {
21339 var styles = node.getAttribute("style").split(";");
21341 Roo.each(styles, function(s) {
21342 if (!s.match(/:/)) {
21345 var kv = s.split(":");
21346 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21349 // what ever is left... we allow.
21352 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21353 if (!nstyle.length) {
21354 node.removeAttribute('style');
21358 this.iterateChildren(node, this.cleanTableWidths);
21366 domToHTML : function(currentElement, depth, nopadtext) {
21368 depth = depth || 0;
21369 nopadtext = nopadtext || false;
21371 if (!currentElement) {
21372 return this.domToHTML(this.doc.body);
21375 //Roo.log(currentElement);
21377 var allText = false;
21378 var nodeName = currentElement.nodeName;
21379 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21381 if (nodeName == '#text') {
21383 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21388 if (nodeName != 'BODY') {
21391 // Prints the node tagName, such as <A>, <IMG>, etc
21394 for(i = 0; i < currentElement.attributes.length;i++) {
21396 var aname = currentElement.attributes.item(i).name;
21397 if (!currentElement.attributes.item(i).value.length) {
21400 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21403 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21412 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21415 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21420 // Traverse the tree
21422 var currentElementChild = currentElement.childNodes.item(i);
21423 var allText = true;
21424 var innerHTML = '';
21426 while (currentElementChild) {
21427 // Formatting code (indent the tree so it looks nice on the screen)
21428 var nopad = nopadtext;
21429 if (lastnode == 'SPAN') {
21433 if (currentElementChild.nodeName == '#text') {
21434 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21435 toadd = nopadtext ? toadd : toadd.trim();
21436 if (!nopad && toadd.length > 80) {
21437 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21439 innerHTML += toadd;
21442 currentElementChild = currentElement.childNodes.item(i);
21448 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21450 // Recursively traverse the tree structure of the child node
21451 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21452 lastnode = currentElementChild.nodeName;
21454 currentElementChild=currentElement.childNodes.item(i);
21460 // The remaining code is mostly for formatting the tree
21461 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21466 ret+= "</"+tagName+">";
21472 applyBlacklists : function()
21474 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21475 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21479 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21480 if (b.indexOf(tag) > -1) {
21483 this.white.push(tag);
21487 Roo.each(w, function(tag) {
21488 if (b.indexOf(tag) > -1) {
21491 if (this.white.indexOf(tag) > -1) {
21494 this.white.push(tag);
21499 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21500 if (w.indexOf(tag) > -1) {
21503 this.black.push(tag);
21507 Roo.each(b, function(tag) {
21508 if (w.indexOf(tag) > -1) {
21511 if (this.black.indexOf(tag) > -1) {
21514 this.black.push(tag);
21519 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21520 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21524 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21525 if (b.indexOf(tag) > -1) {
21528 this.cwhite.push(tag);
21532 Roo.each(w, function(tag) {
21533 if (b.indexOf(tag) > -1) {
21536 if (this.cwhite.indexOf(tag) > -1) {
21539 this.cwhite.push(tag);
21544 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21545 if (w.indexOf(tag) > -1) {
21548 this.cblack.push(tag);
21552 Roo.each(b, function(tag) {
21553 if (w.indexOf(tag) > -1) {
21556 if (this.cblack.indexOf(tag) > -1) {
21559 this.cblack.push(tag);
21564 setStylesheets : function(stylesheets)
21566 if(typeof(stylesheets) == 'string'){
21567 Roo.get(this.iframe.contentDocument.head).createChild({
21569 rel : 'stylesheet',
21578 Roo.each(stylesheets, function(s) {
21583 Roo.get(_this.iframe.contentDocument.head).createChild({
21585 rel : 'stylesheet',
21594 removeStylesheets : function()
21598 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21603 // hide stuff that is not compatible
21617 * @event specialkey
21621 * @cfg {String} fieldClass @hide
21624 * @cfg {String} focusClass @hide
21627 * @cfg {String} autoCreate @hide
21630 * @cfg {String} inputType @hide
21633 * @cfg {String} invalidClass @hide
21636 * @cfg {String} invalidText @hide
21639 * @cfg {String} msgFx @hide
21642 * @cfg {String} validateOnBlur @hide
21646 Roo.HtmlEditorCore.white = [
21647 'area', 'br', 'img', 'input', 'hr', 'wbr',
21649 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21650 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21651 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21652 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21653 'table', 'ul', 'xmp',
21655 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21658 'dir', 'menu', 'ol', 'ul', 'dl',
21664 Roo.HtmlEditorCore.black = [
21665 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21667 'base', 'basefont', 'bgsound', 'blink', 'body',
21668 'frame', 'frameset', 'head', 'html', 'ilayer',
21669 'iframe', 'layer', 'link', 'meta', 'object',
21670 'script', 'style' ,'title', 'xml' // clean later..
21672 Roo.HtmlEditorCore.clean = [
21673 'script', 'style', 'title', 'xml'
21675 Roo.HtmlEditorCore.remove = [
21680 Roo.HtmlEditorCore.ablack = [
21684 Roo.HtmlEditorCore.aclean = [
21685 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21689 Roo.HtmlEditorCore.pwhite= [
21690 'http', 'https', 'mailto'
21693 // white listed style attributes.
21694 Roo.HtmlEditorCore.cwhite= [
21695 // 'text-align', /// default is to allow most things..
21701 // black listed style attributes.
21702 Roo.HtmlEditorCore.cblack= [
21703 // 'font-size' -- this can be set by the project
21707 Roo.HtmlEditorCore.swapCodes =[
21726 * @class Roo.bootstrap.HtmlEditor
21727 * @extends Roo.bootstrap.TextArea
21728 * Bootstrap HtmlEditor class
21731 * Create a new HtmlEditor
21732 * @param {Object} config The config object
21735 Roo.bootstrap.HtmlEditor = function(config){
21736 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21737 if (!this.toolbars) {
21738 this.toolbars = [];
21740 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21743 * @event initialize
21744 * Fires when the editor is fully initialized (including the iframe)
21745 * @param {HtmlEditor} this
21750 * Fires when the editor is first receives the focus. Any insertion must wait
21751 * until after this event.
21752 * @param {HtmlEditor} this
21756 * @event beforesync
21757 * Fires before the textarea is updated with content from the editor iframe. Return false
21758 * to cancel the sync.
21759 * @param {HtmlEditor} this
21760 * @param {String} html
21764 * @event beforepush
21765 * Fires before the iframe editor is updated with content from the textarea. Return false
21766 * to cancel the push.
21767 * @param {HtmlEditor} this
21768 * @param {String} html
21773 * Fires when the textarea is updated with content from the editor iframe.
21774 * @param {HtmlEditor} this
21775 * @param {String} html
21780 * Fires when the iframe editor is updated with content from the textarea.
21781 * @param {HtmlEditor} this
21782 * @param {String} html
21786 * @event editmodechange
21787 * Fires when the editor switches edit modes
21788 * @param {HtmlEditor} this
21789 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21791 editmodechange: true,
21793 * @event editorevent
21794 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21795 * @param {HtmlEditor} this
21799 * @event firstfocus
21800 * Fires when on first focus - needed by toolbars..
21801 * @param {HtmlEditor} this
21806 * Auto save the htmlEditor value as a file into Events
21807 * @param {HtmlEditor} this
21811 * @event savedpreview
21812 * preview the saved version of htmlEditor
21813 * @param {HtmlEditor} this
21820 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21824 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21829 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21834 * @cfg {Number} height (in pixels)
21838 * @cfg {Number} width (in pixels)
21843 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21846 stylesheets: false,
21851 // private properties
21852 validationEvent : false,
21854 initialized : false,
21857 onFocus : Roo.emptyFn,
21859 hideMode:'offsets',
21862 tbContainer : false,
21864 toolbarContainer :function() {
21865 return this.wrap.select('.x-html-editor-tb',true).first();
21869 * Protected method that will not generally be called directly. It
21870 * is called when the editor creates its toolbar. Override this method if you need to
21871 * add custom toolbar buttons.
21872 * @param {HtmlEditor} editor
21874 createToolbar : function(){
21876 Roo.log("create toolbars");
21878 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21879 this.toolbars[0].render(this.toolbarContainer());
21883 // if (!editor.toolbars || !editor.toolbars.length) {
21884 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21887 // for (var i =0 ; i < editor.toolbars.length;i++) {
21888 // editor.toolbars[i] = Roo.factory(
21889 // typeof(editor.toolbars[i]) == 'string' ?
21890 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21891 // Roo.bootstrap.HtmlEditor);
21892 // editor.toolbars[i].init(editor);
21898 onRender : function(ct, position)
21900 // Roo.log("Call onRender: " + this.xtype);
21902 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21904 this.wrap = this.inputEl().wrap({
21905 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21908 this.editorcore.onRender(ct, position);
21910 if (this.resizable) {
21911 this.resizeEl = new Roo.Resizable(this.wrap, {
21915 minHeight : this.height,
21916 height: this.height,
21917 handles : this.resizable,
21920 resize : function(r, w, h) {
21921 _t.onResize(w,h); // -something
21927 this.createToolbar(this);
21930 if(!this.width && this.resizable){
21931 this.setSize(this.wrap.getSize());
21933 if (this.resizeEl) {
21934 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21935 // should trigger onReize..
21941 onResize : function(w, h)
21943 Roo.log('resize: ' +w + ',' + h );
21944 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21948 if(this.inputEl() ){
21949 if(typeof w == 'number'){
21950 var aw = w - this.wrap.getFrameWidth('lr');
21951 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21954 if(typeof h == 'number'){
21955 var tbh = -11; // fixme it needs to tool bar size!
21956 for (var i =0; i < this.toolbars.length;i++) {
21957 // fixme - ask toolbars for heights?
21958 tbh += this.toolbars[i].el.getHeight();
21959 //if (this.toolbars[i].footer) {
21960 // tbh += this.toolbars[i].footer.el.getHeight();
21968 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21969 ah -= 5; // knock a few pixes off for look..
21970 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21974 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21975 this.editorcore.onResize(ew,eh);
21980 * Toggles the editor between standard and source edit mode.
21981 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21983 toggleSourceEdit : function(sourceEditMode)
21985 this.editorcore.toggleSourceEdit(sourceEditMode);
21987 if(this.editorcore.sourceEditMode){
21988 Roo.log('editor - showing textarea');
21991 // Roo.log(this.syncValue());
21993 this.inputEl().removeClass(['hide', 'x-hidden']);
21994 this.inputEl().dom.removeAttribute('tabIndex');
21995 this.inputEl().focus();
21997 Roo.log('editor - hiding textarea');
21999 // Roo.log(this.pushValue());
22002 this.inputEl().addClass(['hide', 'x-hidden']);
22003 this.inputEl().dom.setAttribute('tabIndex', -1);
22004 //this.deferFocus();
22007 if(this.resizable){
22008 this.setSize(this.wrap.getSize());
22011 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22014 // private (for BoxComponent)
22015 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22017 // private (for BoxComponent)
22018 getResizeEl : function(){
22022 // private (for BoxComponent)
22023 getPositionEl : function(){
22028 initEvents : function(){
22029 this.originalValue = this.getValue();
22033 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22036 // markInvalid : Roo.emptyFn,
22038 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22041 // clearInvalid : Roo.emptyFn,
22043 setValue : function(v){
22044 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22045 this.editorcore.pushValue();
22050 deferFocus : function(){
22051 this.focus.defer(10, this);
22055 focus : function(){
22056 this.editorcore.focus();
22062 onDestroy : function(){
22068 for (var i =0; i < this.toolbars.length;i++) {
22069 // fixme - ask toolbars for heights?
22070 this.toolbars[i].onDestroy();
22073 this.wrap.dom.innerHTML = '';
22074 this.wrap.remove();
22079 onFirstFocus : function(){
22080 //Roo.log("onFirstFocus");
22081 this.editorcore.onFirstFocus();
22082 for (var i =0; i < this.toolbars.length;i++) {
22083 this.toolbars[i].onFirstFocus();
22089 syncValue : function()
22091 this.editorcore.syncValue();
22094 pushValue : function()
22096 this.editorcore.pushValue();
22100 // hide stuff that is not compatible
22114 * @event specialkey
22118 * @cfg {String} fieldClass @hide
22121 * @cfg {String} focusClass @hide
22124 * @cfg {String} autoCreate @hide
22127 * @cfg {String} inputType @hide
22130 * @cfg {String} invalidClass @hide
22133 * @cfg {String} invalidText @hide
22136 * @cfg {String} msgFx @hide
22139 * @cfg {String} validateOnBlur @hide
22148 Roo.namespace('Roo.bootstrap.htmleditor');
22150 * @class Roo.bootstrap.HtmlEditorToolbar1
22155 new Roo.bootstrap.HtmlEditor({
22158 new Roo.bootstrap.HtmlEditorToolbar1({
22159 disable : { fonts: 1 , format: 1, ..., ... , ...],
22165 * @cfg {Object} disable List of elements to disable..
22166 * @cfg {Array} btns List of additional buttons.
22170 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22173 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22176 Roo.apply(this, config);
22178 // default disabled, based on 'good practice'..
22179 this.disable = this.disable || {};
22180 Roo.applyIf(this.disable, {
22183 specialElements : true
22185 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22187 this.editor = config.editor;
22188 this.editorcore = config.editor.editorcore;
22190 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22192 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22193 // dont call parent... till later.
22195 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
22200 editorcore : false,
22205 "h1","h2","h3","h4","h5","h6",
22207 "abbr", "acronym", "address", "cite", "samp", "var",
22211 onRender : function(ct, position)
22213 // Roo.log("Call onRender: " + this.xtype);
22215 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22217 this.el.dom.style.marginBottom = '0';
22219 var editorcore = this.editorcore;
22220 var editor= this.editor;
22223 var btn = function(id,cmd , toggle, handler){
22225 var event = toggle ? 'toggle' : 'click';
22230 xns: Roo.bootstrap,
22233 enableToggle:toggle !== false,
22235 pressed : toggle ? false : null,
22238 a.listeners[toggle ? 'toggle' : 'click'] = function() {
22239 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
22248 xns: Roo.bootstrap,
22249 glyphicon : 'font',
22253 xns: Roo.bootstrap,
22257 Roo.each(this.formats, function(f) {
22258 style.menu.items.push({
22260 xns: Roo.bootstrap,
22261 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22266 editorcore.insertTag(this.tagname);
22273 children.push(style);
22276 btn('bold',false,true);
22277 btn('italic',false,true);
22278 btn('align-left', 'justifyleft',true);
22279 btn('align-center', 'justifycenter',true);
22280 btn('align-right' , 'justifyright',true);
22281 btn('link', false, false, function(btn) {
22282 //Roo.log("create link?");
22283 var url = prompt(this.createLinkText, this.defaultLinkValue);
22284 if(url && url != 'http:/'+'/'){
22285 this.editorcore.relayCmd('createlink', url);
22288 btn('list','insertunorderedlist',true);
22289 btn('pencil', false,true, function(btn){
22292 this.toggleSourceEdit(btn.pressed);
22298 xns: Roo.bootstrap,
22303 xns: Roo.bootstrap,
22308 cog.menu.items.push({
22310 xns: Roo.bootstrap,
22311 html : Clean styles,
22316 editorcore.insertTag(this.tagname);
22325 this.xtype = 'NavSimplebar';
22327 for(var i=0;i< children.length;i++) {
22329 this.buttons.add(this.addxtypeChild(children[i]));
22333 editor.on('editorevent', this.updateToolbar, this);
22335 onBtnClick : function(id)
22337 this.editorcore.relayCmd(id);
22338 this.editorcore.focus();
22342 * Protected method that will not generally be called directly. It triggers
22343 * a toolbar update by reading the markup state of the current selection in the editor.
22345 updateToolbar: function(){
22347 if(!this.editorcore.activated){
22348 this.editor.onFirstFocus(); // is this neeed?
22352 var btns = this.buttons;
22353 var doc = this.editorcore.doc;
22354 btns.get('bold').setActive(doc.queryCommandState('bold'));
22355 btns.get('italic').setActive(doc.queryCommandState('italic'));
22356 //btns.get('underline').setActive(doc.queryCommandState('underline'));
22358 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22359 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22360 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22362 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22363 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22366 var ans = this.editorcore.getAllAncestors();
22367 if (this.formatCombo) {
22370 var store = this.formatCombo.store;
22371 this.formatCombo.setValue("");
22372 for (var i =0; i < ans.length;i++) {
22373 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22375 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22383 // hides menus... - so this cant be on a menu...
22384 Roo.bootstrap.MenuMgr.hideAll();
22386 Roo.bootstrap.MenuMgr.hideAll();
22387 //this.editorsyncValue();
22389 onFirstFocus: function() {
22390 this.buttons.each(function(item){
22394 toggleSourceEdit : function(sourceEditMode){
22397 if(sourceEditMode){
22398 Roo.log("disabling buttons");
22399 this.buttons.each( function(item){
22400 if(item.cmd != 'pencil'){
22406 Roo.log("enabling buttons");
22407 if(this.editorcore.initialized){
22408 this.buttons.each( function(item){
22414 Roo.log("calling toggole on editor");
22415 // tell the editor that it's been pressed..
22416 this.editor.toggleSourceEdit(sourceEditMode);
22426 * @class Roo.bootstrap.Table.AbstractSelectionModel
22427 * @extends Roo.util.Observable
22428 * Abstract base class for grid SelectionModels. It provides the interface that should be
22429 * implemented by descendant classes. This class should not be directly instantiated.
22432 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22433 this.locked = false;
22434 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22438 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22439 /** @ignore Called by the grid automatically. Do not call directly. */
22440 init : function(grid){
22446 * Locks the selections.
22449 this.locked = true;
22453 * Unlocks the selections.
22455 unlock : function(){
22456 this.locked = false;
22460 * Returns true if the selections are locked.
22461 * @return {Boolean}
22463 isLocked : function(){
22464 return this.locked;
22468 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22469 * @class Roo.bootstrap.Table.RowSelectionModel
22470 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22471 * It supports multiple selections and keyboard selection/navigation.
22473 * @param {Object} config
22476 Roo.bootstrap.Table.RowSelectionModel = function(config){
22477 Roo.apply(this, config);
22478 this.selections = new Roo.util.MixedCollection(false, function(o){
22483 this.lastActive = false;
22487 * @event selectionchange
22488 * Fires when the selection changes
22489 * @param {SelectionModel} this
22491 "selectionchange" : true,
22493 * @event afterselectionchange
22494 * Fires after the selection changes (eg. by key press or clicking)
22495 * @param {SelectionModel} this
22497 "afterselectionchange" : true,
22499 * @event beforerowselect
22500 * Fires when a row is selected being selected, return false to cancel.
22501 * @param {SelectionModel} this
22502 * @param {Number} rowIndex The selected index
22503 * @param {Boolean} keepExisting False if other selections will be cleared
22505 "beforerowselect" : true,
22508 * Fires when a row is selected.
22509 * @param {SelectionModel} this
22510 * @param {Number} rowIndex The selected index
22511 * @param {Roo.data.Record} r The record
22513 "rowselect" : true,
22515 * @event rowdeselect
22516 * Fires when a row is deselected.
22517 * @param {SelectionModel} this
22518 * @param {Number} rowIndex The selected index
22520 "rowdeselect" : true
22522 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22523 this.locked = false;
22526 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22528 * @cfg {Boolean} singleSelect
22529 * True to allow selection of only one row at a time (defaults to false)
22531 singleSelect : false,
22534 initEvents : function()
22537 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22538 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
22539 //}else{ // allow click to work like normal
22540 // this.grid.on("rowclick", this.handleDragableRowClick, this);
22542 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22543 this.grid.on("rowclick", this.handleMouseDown, this);
22545 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22546 "up" : function(e){
22548 this.selectPrevious(e.shiftKey);
22549 }else if(this.last !== false && this.lastActive !== false){
22550 var last = this.last;
22551 this.selectRange(this.last, this.lastActive-1);
22552 this.grid.getView().focusRow(this.lastActive);
22553 if(last !== false){
22557 this.selectFirstRow();
22559 this.fireEvent("afterselectionchange", this);
22561 "down" : function(e){
22563 this.selectNext(e.shiftKey);
22564 }else if(this.last !== false && this.lastActive !== false){
22565 var last = this.last;
22566 this.selectRange(this.last, this.lastActive+1);
22567 this.grid.getView().focusRow(this.lastActive);
22568 if(last !== false){
22572 this.selectFirstRow();
22574 this.fireEvent("afterselectionchange", this);
22578 this.grid.store.on('load', function(){
22579 this.selections.clear();
22582 var view = this.grid.view;
22583 view.on("refresh", this.onRefresh, this);
22584 view.on("rowupdated", this.onRowUpdated, this);
22585 view.on("rowremoved", this.onRemove, this);
22590 onRefresh : function()
22592 var ds = this.grid.store, i, v = this.grid.view;
22593 var s = this.selections;
22594 s.each(function(r){
22595 if((i = ds.indexOfId(r.id)) != -1){
22604 onRemove : function(v, index, r){
22605 this.selections.remove(r);
22609 onRowUpdated : function(v, index, r){
22610 if(this.isSelected(r)){
22611 v.onRowSelect(index);
22617 * @param {Array} records The records to select
22618 * @param {Boolean} keepExisting (optional) True to keep existing selections
22620 selectRecords : function(records, keepExisting)
22623 this.clearSelections();
22625 var ds = this.grid.store;
22626 for(var i = 0, len = records.length; i < len; i++){
22627 this.selectRow(ds.indexOf(records[i]), true);
22632 * Gets the number of selected rows.
22635 getCount : function(){
22636 return this.selections.length;
22640 * Selects the first row in the grid.
22642 selectFirstRow : function(){
22647 * Select the last row.
22648 * @param {Boolean} keepExisting (optional) True to keep existing selections
22650 selectLastRow : function(keepExisting){
22651 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22652 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22656 * Selects the row immediately following the last selected row.
22657 * @param {Boolean} keepExisting (optional) True to keep existing selections
22659 selectNext : function(keepExisting)
22661 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22662 this.selectRow(this.last+1, keepExisting);
22663 this.grid.getView().focusRow(this.last);
22668 * Selects the row that precedes the last selected row.
22669 * @param {Boolean} keepExisting (optional) True to keep existing selections
22671 selectPrevious : function(keepExisting){
22673 this.selectRow(this.last-1, keepExisting);
22674 this.grid.getView().focusRow(this.last);
22679 * Returns the selected records
22680 * @return {Array} Array of selected records
22682 getSelections : function(){
22683 return [].concat(this.selections.items);
22687 * Returns the first selected record.
22690 getSelected : function(){
22691 return this.selections.itemAt(0);
22696 * Clears all selections.
22698 clearSelections : function(fast)
22704 var ds = this.grid.store;
22705 var s = this.selections;
22706 s.each(function(r){
22707 this.deselectRow(ds.indexOfId(r.id));
22711 this.selections.clear();
22718 * Selects all rows.
22720 selectAll : function(){
22724 this.selections.clear();
22725 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
22726 this.selectRow(i, true);
22731 * Returns True if there is a selection.
22732 * @return {Boolean}
22734 hasSelection : function(){
22735 return this.selections.length > 0;
22739 * Returns True if the specified row is selected.
22740 * @param {Number/Record} record The record or index of the record to check
22741 * @return {Boolean}
22743 isSelected : function(index){
22744 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
22745 return (r && this.selections.key(r.id) ? true : false);
22749 * Returns True if the specified record id is selected.
22750 * @param {String} id The id of record to check
22751 * @return {Boolean}
22753 isIdSelected : function(id){
22754 return (this.selections.key(id) ? true : false);
22759 handleMouseDBClick : function(e, t){
22763 handleMouseDown : function(e, t)
22765 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
22766 if(this.isLocked() || rowIndex < 0 ){
22769 if(e.shiftKey && this.last !== false){
22770 var last = this.last;
22771 this.selectRange(last, rowIndex, e.ctrlKey);
22772 this.last = last; // reset the last
22776 var isSelected = this.isSelected(rowIndex);
22777 //Roo.log("select row:" + rowIndex);
22779 this.deselectRow(rowIndex);
22781 this.selectRow(rowIndex, true);
22785 if(e.button !== 0 && isSelected){
22786 alert('rowIndex 2: ' + rowIndex);
22787 view.focusRow(rowIndex);
22788 }else if(e.ctrlKey && isSelected){
22789 this.deselectRow(rowIndex);
22790 }else if(!isSelected){
22791 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22792 view.focusRow(rowIndex);
22796 this.fireEvent("afterselectionchange", this);
22799 handleDragableRowClick : function(grid, rowIndex, e)
22801 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22802 this.selectRow(rowIndex, false);
22803 grid.view.focusRow(rowIndex);
22804 this.fireEvent("afterselectionchange", this);
22809 * Selects multiple rows.
22810 * @param {Array} rows Array of the indexes of the row to select
22811 * @param {Boolean} keepExisting (optional) True to keep existing selections
22813 selectRows : function(rows, keepExisting){
22815 this.clearSelections();
22817 for(var i = 0, len = rows.length; i < len; i++){
22818 this.selectRow(rows[i], true);
22823 * Selects a range of rows. All rows in between startRow and endRow are also selected.
22824 * @param {Number} startRow The index of the first row in the range
22825 * @param {Number} endRow The index of the last row in the range
22826 * @param {Boolean} keepExisting (optional) True to retain existing selections
22828 selectRange : function(startRow, endRow, keepExisting){
22833 this.clearSelections();
22835 if(startRow <= endRow){
22836 for(var i = startRow; i <= endRow; i++){
22837 this.selectRow(i, true);
22840 for(var i = startRow; i >= endRow; i--){
22841 this.selectRow(i, true);
22847 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22848 * @param {Number} startRow The index of the first row in the range
22849 * @param {Number} endRow The index of the last row in the range
22851 deselectRange : function(startRow, endRow, preventViewNotify){
22855 for(var i = startRow; i <= endRow; i++){
22856 this.deselectRow(i, preventViewNotify);
22862 * @param {Number} row The index of the row to select
22863 * @param {Boolean} keepExisting (optional) True to keep existing selections
22865 selectRow : function(index, keepExisting, preventViewNotify)
22867 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
22870 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22871 if(!keepExisting || this.singleSelect){
22872 this.clearSelections();
22875 var r = this.grid.store.getAt(index);
22876 //console.log('selectRow - record id :' + r.id);
22878 this.selections.add(r);
22879 this.last = this.lastActive = index;
22880 if(!preventViewNotify){
22881 var proxy = new Roo.Element(
22882 this.grid.getRowDom(index)
22884 proxy.addClass('bg-info info');
22886 this.fireEvent("rowselect", this, index, r);
22887 this.fireEvent("selectionchange", this);
22893 * @param {Number} row The index of the row to deselect
22895 deselectRow : function(index, preventViewNotify)
22900 if(this.last == index){
22903 if(this.lastActive == index){
22904 this.lastActive = false;
22907 var r = this.grid.store.getAt(index);
22912 this.selections.remove(r);
22913 //.console.log('deselectRow - record id :' + r.id);
22914 if(!preventViewNotify){
22916 var proxy = new Roo.Element(
22917 this.grid.getRowDom(index)
22919 proxy.removeClass('bg-info info');
22921 this.fireEvent("rowdeselect", this, index);
22922 this.fireEvent("selectionchange", this);
22926 restoreLast : function(){
22928 this.last = this._last;
22933 acceptsNav : function(row, col, cm){
22934 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22938 onEditorKey : function(field, e){
22939 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22944 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22946 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22948 }else if(k == e.ENTER && !e.ctrlKey){
22952 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22954 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22956 }else if(k == e.ESC){
22960 g.startEditing(newCell[0], newCell[1]);
22966 * Ext JS Library 1.1.1
22967 * Copyright(c) 2006-2007, Ext JS, LLC.
22969 * Originally Released Under LGPL - original licence link has changed is not relivant.
22972 * <script type="text/javascript">
22976 * @class Roo.bootstrap.PagingToolbar
22977 * @extends Roo.bootstrap.NavSimplebar
22978 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22980 * Create a new PagingToolbar
22981 * @param {Object} config The config object
22982 * @param {Roo.data.Store} store
22984 Roo.bootstrap.PagingToolbar = function(config)
22986 // old args format still supported... - xtype is prefered..
22987 // created from xtype...
22989 this.ds = config.dataSource;
22991 if (config.store && !this.ds) {
22992 this.store= Roo.factory(config.store, Roo.data);
22993 this.ds = this.store;
22994 this.ds.xmodule = this.xmodule || false;
22997 this.toolbarItems = [];
22998 if (config.items) {
22999 this.toolbarItems = config.items;
23002 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23007 this.bind(this.ds);
23010 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23014 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23016 * @cfg {Roo.data.Store} dataSource
23017 * The underlying data store providing the paged data
23020 * @cfg {String/HTMLElement/Element} container
23021 * container The id or element that will contain the toolbar
23024 * @cfg {Boolean} displayInfo
23025 * True to display the displayMsg (defaults to false)
23028 * @cfg {Number} pageSize
23029 * The number of records to display per page (defaults to 20)
23033 * @cfg {String} displayMsg
23034 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23036 displayMsg : 'Displaying {0} - {1} of {2}',
23038 * @cfg {String} emptyMsg
23039 * The message to display when no records are found (defaults to "No data to display")
23041 emptyMsg : 'No data to display',
23043 * Customizable piece of the default paging text (defaults to "Page")
23046 beforePageText : "Page",
23048 * Customizable piece of the default paging text (defaults to "of %0")
23051 afterPageText : "of {0}",
23053 * Customizable piece of the default paging text (defaults to "First Page")
23056 firstText : "First Page",
23058 * Customizable piece of the default paging text (defaults to "Previous Page")
23061 prevText : "Previous Page",
23063 * Customizable piece of the default paging text (defaults to "Next Page")
23066 nextText : "Next Page",
23068 * Customizable piece of the default paging text (defaults to "Last Page")
23071 lastText : "Last Page",
23073 * Customizable piece of the default paging text (defaults to "Refresh")
23076 refreshText : "Refresh",
23080 onRender : function(ct, position)
23082 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23083 this.navgroup.parentId = this.id;
23084 this.navgroup.onRender(this.el, null);
23085 // add the buttons to the navgroup
23087 if(this.displayInfo){
23088 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23089 this.displayEl = this.el.select('.x-paging-info', true).first();
23090 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23091 // this.displayEl = navel.el.select('span',true).first();
23097 Roo.each(_this.buttons, function(e){ // this might need to use render????
23098 Roo.factory(e).onRender(_this.el, null);
23102 Roo.each(_this.toolbarItems, function(e) {
23103 _this.navgroup.addItem(e);
23107 this.first = this.navgroup.addItem({
23108 tooltip: this.firstText,
23110 icon : 'fa fa-backward',
23112 preventDefault: true,
23113 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23116 this.prev = this.navgroup.addItem({
23117 tooltip: this.prevText,
23119 icon : 'fa fa-step-backward',
23121 preventDefault: true,
23122 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
23124 //this.addSeparator();
23127 var field = this.navgroup.addItem( {
23129 cls : 'x-paging-position',
23131 html : this.beforePageText +
23132 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23133 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
23136 this.field = field.el.select('input', true).first();
23137 this.field.on("keydown", this.onPagingKeydown, this);
23138 this.field.on("focus", function(){this.dom.select();});
23141 this.afterTextEl = field.el.select('.x-paging-after',true).first();
23142 //this.field.setHeight(18);
23143 //this.addSeparator();
23144 this.next = this.navgroup.addItem({
23145 tooltip: this.nextText,
23147 html : ' <i class="fa fa-step-forward">',
23149 preventDefault: true,
23150 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
23152 this.last = this.navgroup.addItem({
23153 tooltip: this.lastText,
23154 icon : 'fa fa-forward',
23157 preventDefault: true,
23158 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
23160 //this.addSeparator();
23161 this.loading = this.navgroup.addItem({
23162 tooltip: this.refreshText,
23163 icon: 'fa fa-refresh',
23164 preventDefault: true,
23165 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23171 updateInfo : function(){
23172 if(this.displayEl){
23173 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23174 var msg = count == 0 ?
23178 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
23180 this.displayEl.update(msg);
23185 onLoad : function(ds, r, o){
23186 this.cursor = o.params ? o.params.start : 0;
23187 var d = this.getPageData(),
23191 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23192 this.field.dom.value = ap;
23193 this.first.setDisabled(ap == 1);
23194 this.prev.setDisabled(ap == 1);
23195 this.next.setDisabled(ap == ps);
23196 this.last.setDisabled(ap == ps);
23197 this.loading.enable();
23202 getPageData : function(){
23203 var total = this.ds.getTotalCount();
23206 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23207 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23212 onLoadError : function(){
23213 this.loading.enable();
23217 onPagingKeydown : function(e){
23218 var k = e.getKey();
23219 var d = this.getPageData();
23221 var v = this.field.dom.value, pageNum;
23222 if(!v || isNaN(pageNum = parseInt(v, 10))){
23223 this.field.dom.value = d.activePage;
23226 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23227 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23230 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))
23232 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23233 this.field.dom.value = pageNum;
23234 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23237 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23239 var v = this.field.dom.value, pageNum;
23240 var increment = (e.shiftKey) ? 10 : 1;
23241 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23244 if(!v || isNaN(pageNum = parseInt(v, 10))) {
23245 this.field.dom.value = d.activePage;
23248 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23250 this.field.dom.value = parseInt(v, 10) + increment;
23251 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23252 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23259 beforeLoad : function(){
23261 this.loading.disable();
23266 onClick : function(which){
23275 ds.load({params:{start: 0, limit: this.pageSize}});
23278 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23281 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23284 var total = ds.getTotalCount();
23285 var extra = total % this.pageSize;
23286 var lastStart = extra ? (total - extra) : total-this.pageSize;
23287 ds.load({params:{start: lastStart, limit: this.pageSize}});
23290 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23296 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23297 * @param {Roo.data.Store} store The data store to unbind
23299 unbind : function(ds){
23300 ds.un("beforeload", this.beforeLoad, this);
23301 ds.un("load", this.onLoad, this);
23302 ds.un("loadexception", this.onLoadError, this);
23303 ds.un("remove", this.updateInfo, this);
23304 ds.un("add", this.updateInfo, this);
23305 this.ds = undefined;
23309 * Binds the paging toolbar to the specified {@link Roo.data.Store}
23310 * @param {Roo.data.Store} store The data store to bind
23312 bind : function(ds){
23313 ds.on("beforeload", this.beforeLoad, this);
23314 ds.on("load", this.onLoad, this);
23315 ds.on("loadexception", this.onLoadError, this);
23316 ds.on("remove", this.updateInfo, this);
23317 ds.on("add", this.updateInfo, this);
23328 * @class Roo.bootstrap.MessageBar
23329 * @extends Roo.bootstrap.Component
23330 * Bootstrap MessageBar class
23331 * @cfg {String} html contents of the MessageBar
23332 * @cfg {String} weight (info | success | warning | danger) default info
23333 * @cfg {String} beforeClass insert the bar before the given class
23334 * @cfg {Boolean} closable (true | false) default false
23335 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23338 * Create a new Element
23339 * @param {Object} config The config object
23342 Roo.bootstrap.MessageBar = function(config){
23343 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23346 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
23352 beforeClass: 'bootstrap-sticky-wrap',
23354 getAutoCreate : function(){
23358 cls: 'alert alert-dismissable alert-' + this.weight,
23363 html: this.html || ''
23369 cfg.cls += ' alert-messages-fixed';
23383 onRender : function(ct, position)
23385 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23388 var cfg = Roo.apply({}, this.getAutoCreate());
23392 cfg.cls += ' ' + this.cls;
23395 cfg.style = this.style;
23397 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23399 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23402 this.el.select('>button.close').on('click', this.hide, this);
23408 if (!this.rendered) {
23414 this.fireEvent('show', this);
23420 if (!this.rendered) {
23426 this.fireEvent('hide', this);
23429 update : function()
23431 // var e = this.el.dom.firstChild;
23433 // if(this.closable){
23434 // e = e.nextSibling;
23437 // e.data = this.html || '';
23439 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23455 * @class Roo.bootstrap.Graph
23456 * @extends Roo.bootstrap.Component
23457 * Bootstrap Graph class
23461 @cfg {String} graphtype bar | vbar | pie
23462 @cfg {number} g_x coodinator | centre x (pie)
23463 @cfg {number} g_y coodinator | centre y (pie)
23464 @cfg {number} g_r radius (pie)
23465 @cfg {number} g_height height of the chart (respected by all elements in the set)
23466 @cfg {number} g_width width of the chart (respected by all elements in the set)
23467 @cfg {Object} title The title of the chart
23470 -opts (object) options for the chart
23472 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23473 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23475 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.
23476 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23478 o stretch (boolean)
23480 -opts (object) options for the pie
23483 o startAngle (number)
23484 o endAngle (number)
23488 * Create a new Input
23489 * @param {Object} config The config object
23492 Roo.bootstrap.Graph = function(config){
23493 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23499 * The img click event for the img.
23500 * @param {Roo.EventObject} e
23506 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23517 //g_colors: this.colors,
23524 getAutoCreate : function(){
23535 onRender : function(ct,position){
23538 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23540 if (typeof(Raphael) == 'undefined') {
23541 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23545 this.raphael = Raphael(this.el.dom);
23547 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23548 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23549 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23550 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23552 r.text(160, 10, "Single Series Chart").attr(txtattr);
23553 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23554 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23555 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23557 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23558 r.barchart(330, 10, 300, 220, data1);
23559 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23560 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23563 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23564 // r.barchart(30, 30, 560, 250, xdata, {
23565 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23566 // axis : "0 0 1 1",
23567 // axisxlabels : xdata
23568 // //yvalues : cols,
23571 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23573 // this.load(null,xdata,{
23574 // axis : "0 0 1 1",
23575 // axisxlabels : xdata
23580 load : function(graphtype,xdata,opts)
23582 this.raphael.clear();
23584 graphtype = this.graphtype;
23589 var r = this.raphael,
23590 fin = function () {
23591 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23593 fout = function () {
23594 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23596 pfin = function() {
23597 this.sector.stop();
23598 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23601 this.label[0].stop();
23602 this.label[0].attr({ r: 7.5 });
23603 this.label[1].attr({ "font-weight": 800 });
23606 pfout = function() {
23607 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23610 this.label[0].animate({ r: 5 }, 500, "bounce");
23611 this.label[1].attr({ "font-weight": 400 });
23617 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23620 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23623 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23624 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23626 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23633 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23638 setTitle: function(o)
23643 initEvents: function() {
23646 this.el.on('click', this.onClick, this);
23650 onClick : function(e)
23652 Roo.log('img onclick');
23653 this.fireEvent('click', this, e);
23665 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23668 * @class Roo.bootstrap.dash.NumberBox
23669 * @extends Roo.bootstrap.Component
23670 * Bootstrap NumberBox class
23671 * @cfg {String} headline Box headline
23672 * @cfg {String} content Box content
23673 * @cfg {String} icon Box icon
23674 * @cfg {String} footer Footer text
23675 * @cfg {String} fhref Footer href
23678 * Create a new NumberBox
23679 * @param {Object} config The config object
23683 Roo.bootstrap.dash.NumberBox = function(config){
23684 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23688 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23697 getAutoCreate : function(){
23701 cls : 'small-box ',
23709 cls : 'roo-headline',
23710 html : this.headline
23714 cls : 'roo-content',
23715 html : this.content
23729 cls : 'ion ' + this.icon
23738 cls : 'small-box-footer',
23739 href : this.fhref || '#',
23743 cfg.cn.push(footer);
23750 onRender : function(ct,position){
23751 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23758 setHeadline: function (value)
23760 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23763 setFooter: function (value, href)
23765 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23768 this.el.select('a.small-box-footer',true).first().attr('href', href);
23773 setContent: function (value)
23775 this.el.select('.roo-content',true).first().dom.innerHTML = value;
23778 initEvents: function()
23792 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23795 * @class Roo.bootstrap.dash.TabBox
23796 * @extends Roo.bootstrap.Component
23797 * Bootstrap TabBox class
23798 * @cfg {String} title Title of the TabBox
23799 * @cfg {String} icon Icon of the TabBox
23800 * @cfg {Boolean} showtabs (true|false) show the tabs default true
23801 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23804 * Create a new TabBox
23805 * @param {Object} config The config object
23809 Roo.bootstrap.dash.TabBox = function(config){
23810 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23815 * When a pane is added
23816 * @param {Roo.bootstrap.dash.TabPane} pane
23820 * @event activatepane
23821 * When a pane is activated
23822 * @param {Roo.bootstrap.dash.TabPane} pane
23824 "activatepane" : true
23832 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
23837 tabScrollable : false,
23839 getChildContainer : function()
23841 return this.el.select('.tab-content', true).first();
23844 getAutoCreate : function(){
23848 cls: 'pull-left header',
23856 cls: 'fa ' + this.icon
23862 cls: 'nav nav-tabs pull-right',
23868 if(this.tabScrollable){
23875 cls: 'nav nav-tabs pull-right',
23886 cls: 'nav-tabs-custom',
23891 cls: 'tab-content no-padding',
23899 initEvents : function()
23901 //Roo.log('add add pane handler');
23902 this.on('addpane', this.onAddPane, this);
23905 * Updates the box title
23906 * @param {String} html to set the title to.
23908 setTitle : function(value)
23910 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23912 onAddPane : function(pane)
23914 this.panes.push(pane);
23915 //Roo.log('addpane');
23917 // tabs are rendere left to right..
23918 if(!this.showtabs){
23922 var ctr = this.el.select('.nav-tabs', true).first();
23925 var existing = ctr.select('.nav-tab',true);
23926 var qty = existing.getCount();;
23929 var tab = ctr.createChild({
23931 cls : 'nav-tab' + (qty ? '' : ' active'),
23939 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23942 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23944 pane.el.addClass('active');
23949 onTabClick : function(ev,un,ob,pane)
23951 //Roo.log('tab - prev default');
23952 ev.preventDefault();
23955 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23956 pane.tab.addClass('active');
23957 //Roo.log(pane.title);
23958 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23959 // technically we should have a deactivate event.. but maybe add later.
23960 // and it should not de-activate the selected tab...
23961 this.fireEvent('activatepane', pane);
23962 pane.el.addClass('active');
23963 pane.fireEvent('activate');
23968 getActivePane : function()
23971 Roo.each(this.panes, function(p) {
23972 if(p.el.hasClass('active')){
23993 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23995 * @class Roo.bootstrap.TabPane
23996 * @extends Roo.bootstrap.Component
23997 * Bootstrap TabPane class
23998 * @cfg {Boolean} active (false | true) Default false
23999 * @cfg {String} title title of panel
24003 * Create a new TabPane
24004 * @param {Object} config The config object
24007 Roo.bootstrap.dash.TabPane = function(config){
24008 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24014 * When a pane is activated
24015 * @param {Roo.bootstrap.dash.TabPane} pane
24022 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
24027 // the tabBox that this is attached to.
24030 getAutoCreate : function()
24038 cfg.cls += ' active';
24043 initEvents : function()
24045 //Roo.log('trigger add pane handler');
24046 this.parent().fireEvent('addpane', this)
24050 * Updates the tab title
24051 * @param {String} html to set the title to.
24053 setTitle: function(str)
24059 this.tab.select('a', true).first().dom.innerHTML = str;
24076 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24079 * @class Roo.bootstrap.menu.Menu
24080 * @extends Roo.bootstrap.Component
24081 * Bootstrap Menu class - container for Menu
24082 * @cfg {String} html Text of the menu
24083 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24084 * @cfg {String} icon Font awesome icon
24085 * @cfg {String} pos Menu align to (top | bottom) default bottom
24089 * Create a new Menu
24090 * @param {Object} config The config object
24094 Roo.bootstrap.menu.Menu = function(config){
24095 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24099 * @event beforeshow
24100 * Fires before this menu is displayed
24101 * @param {Roo.bootstrap.menu.Menu} this
24105 * @event beforehide
24106 * Fires before this menu is hidden
24107 * @param {Roo.bootstrap.menu.Menu} this
24112 * Fires after this menu is displayed
24113 * @param {Roo.bootstrap.menu.Menu} this
24118 * Fires after this menu is hidden
24119 * @param {Roo.bootstrap.menu.Menu} this
24124 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24125 * @param {Roo.bootstrap.menu.Menu} this
24126 * @param {Roo.EventObject} e
24133 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
24137 weight : 'default',
24142 getChildContainer : function() {
24143 if(this.isSubMenu){
24147 return this.el.select('ul.dropdown-menu', true).first();
24150 getAutoCreate : function()
24155 cls : 'roo-menu-text',
24163 cls : 'fa ' + this.icon
24174 cls : 'dropdown-button btn btn-' + this.weight,
24179 cls : 'dropdown-toggle btn btn-' + this.weight,
24189 cls : 'dropdown-menu'
24195 if(this.pos == 'top'){
24196 cfg.cls += ' dropup';
24199 if(this.isSubMenu){
24202 cls : 'dropdown-menu'
24209 onRender : function(ct, position)
24211 this.isSubMenu = ct.hasClass('dropdown-submenu');
24213 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24216 initEvents : function()
24218 if(this.isSubMenu){
24222 this.hidden = true;
24224 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24225 this.triggerEl.on('click', this.onTriggerPress, this);
24227 this.buttonEl = this.el.select('button.dropdown-button', true).first();
24228 this.buttonEl.on('click', this.onClick, this);
24234 if(this.isSubMenu){
24238 return this.el.select('ul.dropdown-menu', true).first();
24241 onClick : function(e)
24243 this.fireEvent("click", this, e);
24246 onTriggerPress : function(e)
24248 if (this.isVisible()) {
24255 isVisible : function(){
24256 return !this.hidden;
24261 this.fireEvent("beforeshow", this);
24263 this.hidden = false;
24264 this.el.addClass('open');
24266 Roo.get(document).on("mouseup", this.onMouseUp, this);
24268 this.fireEvent("show", this);
24275 this.fireEvent("beforehide", this);
24277 this.hidden = true;
24278 this.el.removeClass('open');
24280 Roo.get(document).un("mouseup", this.onMouseUp);
24282 this.fireEvent("hide", this);
24285 onMouseUp : function()
24299 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24302 * @class Roo.bootstrap.menu.Item
24303 * @extends Roo.bootstrap.Component
24304 * Bootstrap MenuItem class
24305 * @cfg {Boolean} submenu (true | false) default false
24306 * @cfg {String} html text of the item
24307 * @cfg {String} href the link
24308 * @cfg {Boolean} disable (true | false) default false
24309 * @cfg {Boolean} preventDefault (true | false) default true
24310 * @cfg {String} icon Font awesome icon
24311 * @cfg {String} pos Submenu align to (left | right) default right
24315 * Create a new Item
24316 * @param {Object} config The config object
24320 Roo.bootstrap.menu.Item = function(config){
24321 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24325 * Fires when the mouse is hovering over this menu
24326 * @param {Roo.bootstrap.menu.Item} this
24327 * @param {Roo.EventObject} e
24332 * Fires when the mouse exits this menu
24333 * @param {Roo.bootstrap.menu.Item} this
24334 * @param {Roo.EventObject} e
24340 * The raw click event for the entire grid.
24341 * @param {Roo.EventObject} e
24347 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
24352 preventDefault: true,
24357 getAutoCreate : function()
24362 cls : 'roo-menu-item-text',
24370 cls : 'fa ' + this.icon
24379 href : this.href || '#',
24386 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24390 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24392 if(this.pos == 'left'){
24393 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24400 initEvents : function()
24402 this.el.on('mouseover', this.onMouseOver, this);
24403 this.el.on('mouseout', this.onMouseOut, this);
24405 this.el.select('a', true).first().on('click', this.onClick, this);
24409 onClick : function(e)
24411 if(this.preventDefault){
24412 e.preventDefault();
24415 this.fireEvent("click", this, e);
24418 onMouseOver : function(e)
24420 if(this.submenu && this.pos == 'left'){
24421 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24424 this.fireEvent("mouseover", this, e);
24427 onMouseOut : function(e)
24429 this.fireEvent("mouseout", this, e);
24441 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24444 * @class Roo.bootstrap.menu.Separator
24445 * @extends Roo.bootstrap.Component
24446 * Bootstrap Separator class
24449 * Create a new Separator
24450 * @param {Object} config The config object
24454 Roo.bootstrap.menu.Separator = function(config){
24455 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24458 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24460 getAutoCreate : function(){
24481 * @class Roo.bootstrap.Tooltip
24482 * Bootstrap Tooltip class
24483 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24484 * to determine which dom element triggers the tooltip.
24486 * It needs to add support for additional attributes like tooltip-position
24489 * Create a new Toolti
24490 * @param {Object} config The config object
24493 Roo.bootstrap.Tooltip = function(config){
24494 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24497 Roo.apply(Roo.bootstrap.Tooltip, {
24499 * @function init initialize tooltip monitoring.
24503 currentTip : false,
24504 currentRegion : false,
24510 Roo.get(document).on('mouseover', this.enter ,this);
24511 Roo.get(document).on('mouseout', this.leave, this);
24514 this.currentTip = new Roo.bootstrap.Tooltip();
24517 enter : function(ev)
24519 var dom = ev.getTarget();
24521 //Roo.log(['enter',dom]);
24522 var el = Roo.fly(dom);
24523 if (this.currentEl) {
24525 //Roo.log(this.currentEl);
24526 //Roo.log(this.currentEl.contains(dom));
24527 if (this.currentEl == el) {
24530 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24536 if (this.currentTip.el) {
24537 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24541 if(!el || el.dom == document){
24547 // you can not look for children, as if el is the body.. then everythign is the child..
24548 if (!el.attr('tooltip')) { //
24549 if (!el.select("[tooltip]").elements.length) {
24552 // is the mouse over this child...?
24553 bindEl = el.select("[tooltip]").first();
24554 var xy = ev.getXY();
24555 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24556 //Roo.log("not in region.");
24559 //Roo.log("child element over..");
24562 this.currentEl = bindEl;
24563 this.currentTip.bind(bindEl);
24564 this.currentRegion = Roo.lib.Region.getRegion(dom);
24565 this.currentTip.enter();
24568 leave : function(ev)
24570 var dom = ev.getTarget();
24571 //Roo.log(['leave',dom]);
24572 if (!this.currentEl) {
24577 if (dom != this.currentEl.dom) {
24580 var xy = ev.getXY();
24581 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24584 // only activate leave if mouse cursor is outside... bounding box..
24589 if (this.currentTip) {
24590 this.currentTip.leave();
24592 //Roo.log('clear currentEl');
24593 this.currentEl = false;
24598 'left' : ['r-l', [-2,0], 'right'],
24599 'right' : ['l-r', [2,0], 'left'],
24600 'bottom' : ['t-b', [0,2], 'top'],
24601 'top' : [ 'b-t', [0,-2], 'bottom']
24607 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24612 delay : null, // can be { show : 300 , hide: 500}
24616 hoverState : null, //???
24618 placement : 'bottom',
24620 getAutoCreate : function(){
24627 cls : 'tooltip-arrow'
24630 cls : 'tooltip-inner'
24637 bind : function(el)
24643 enter : function () {
24645 if (this.timeout != null) {
24646 clearTimeout(this.timeout);
24649 this.hoverState = 'in';
24650 //Roo.log("enter - show");
24651 if (!this.delay || !this.delay.show) {
24656 this.timeout = setTimeout(function () {
24657 if (_t.hoverState == 'in') {
24660 }, this.delay.show);
24664 clearTimeout(this.timeout);
24666 this.hoverState = 'out';
24667 if (!this.delay || !this.delay.hide) {
24673 this.timeout = setTimeout(function () {
24674 //Roo.log("leave - timeout");
24676 if (_t.hoverState == 'out') {
24678 Roo.bootstrap.Tooltip.currentEl = false;
24686 this.render(document.body);
24689 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24691 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24693 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24695 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24697 var placement = typeof this.placement == 'function' ?
24698 this.placement.call(this, this.el, on_el) :
24701 var autoToken = /\s?auto?\s?/i;
24702 var autoPlace = autoToken.test(placement);
24704 placement = placement.replace(autoToken, '') || 'top';
24708 //this.el.setXY([0,0]);
24710 //this.el.dom.style.display='block';
24712 //this.el.appendTo(on_el);
24714 var p = this.getPosition();
24715 var box = this.el.getBox();
24721 var align = Roo.bootstrap.Tooltip.alignment[placement];
24723 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24725 if(placement == 'top' || placement == 'bottom'){
24727 placement = 'right';
24730 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24731 placement = 'left';
24734 var scroll = Roo.select('body', true).first().getScroll();
24736 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24742 align = Roo.bootstrap.Tooltip.alignment[placement];
24744 this.el.alignTo(this.bindEl, align[0],align[1]);
24745 //var arrow = this.el.select('.arrow',true).first();
24746 //arrow.set(align[2],
24748 this.el.addClass(placement);
24750 this.el.addClass('in fade');
24752 this.hoverState = null;
24754 if (this.el.hasClass('fade')) {
24765 //this.el.setXY([0,0]);
24766 this.el.removeClass('in');
24782 * @class Roo.bootstrap.LocationPicker
24783 * @extends Roo.bootstrap.Component
24784 * Bootstrap LocationPicker class
24785 * @cfg {Number} latitude Position when init default 0
24786 * @cfg {Number} longitude Position when init default 0
24787 * @cfg {Number} zoom default 15
24788 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24789 * @cfg {Boolean} mapTypeControl default false
24790 * @cfg {Boolean} disableDoubleClickZoom default false
24791 * @cfg {Boolean} scrollwheel default true
24792 * @cfg {Boolean} streetViewControl default false
24793 * @cfg {Number} radius default 0
24794 * @cfg {String} locationName
24795 * @cfg {Boolean} draggable default true
24796 * @cfg {Boolean} enableAutocomplete default false
24797 * @cfg {Boolean} enableReverseGeocode default true
24798 * @cfg {String} markerTitle
24801 * Create a new LocationPicker
24802 * @param {Object} config The config object
24806 Roo.bootstrap.LocationPicker = function(config){
24808 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24813 * Fires when the picker initialized.
24814 * @param {Roo.bootstrap.LocationPicker} this
24815 * @param {Google Location} location
24819 * @event positionchanged
24820 * Fires when the picker position changed.
24821 * @param {Roo.bootstrap.LocationPicker} this
24822 * @param {Google Location} location
24824 positionchanged : true,
24827 * Fires when the map resize.
24828 * @param {Roo.bootstrap.LocationPicker} this
24833 * Fires when the map show.
24834 * @param {Roo.bootstrap.LocationPicker} this
24839 * Fires when the map hide.
24840 * @param {Roo.bootstrap.LocationPicker} this
24845 * Fires when click the map.
24846 * @param {Roo.bootstrap.LocationPicker} this
24847 * @param {Map event} e
24851 * @event mapRightClick
24852 * Fires when right click the map.
24853 * @param {Roo.bootstrap.LocationPicker} this
24854 * @param {Map event} e
24856 mapRightClick : true,
24858 * @event markerClick
24859 * Fires when click the marker.
24860 * @param {Roo.bootstrap.LocationPicker} this
24861 * @param {Map event} e
24863 markerClick : true,
24865 * @event markerRightClick
24866 * Fires when right click the marker.
24867 * @param {Roo.bootstrap.LocationPicker} this
24868 * @param {Map event} e
24870 markerRightClick : true,
24872 * @event OverlayViewDraw
24873 * Fires when OverlayView Draw
24874 * @param {Roo.bootstrap.LocationPicker} this
24876 OverlayViewDraw : true,
24878 * @event OverlayViewOnAdd
24879 * Fires when OverlayView Draw
24880 * @param {Roo.bootstrap.LocationPicker} this
24882 OverlayViewOnAdd : true,
24884 * @event OverlayViewOnRemove
24885 * Fires when OverlayView Draw
24886 * @param {Roo.bootstrap.LocationPicker} this
24888 OverlayViewOnRemove : true,
24890 * @event OverlayViewShow
24891 * Fires when OverlayView Draw
24892 * @param {Roo.bootstrap.LocationPicker} this
24893 * @param {Pixel} cpx
24895 OverlayViewShow : true,
24897 * @event OverlayViewHide
24898 * Fires when OverlayView Draw
24899 * @param {Roo.bootstrap.LocationPicker} this
24901 OverlayViewHide : true,
24903 * @event loadexception
24904 * Fires when load google lib failed.
24905 * @param {Roo.bootstrap.LocationPicker} this
24907 loadexception : true
24912 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24914 gMapContext: false,
24920 mapTypeControl: false,
24921 disableDoubleClickZoom: false,
24923 streetViewControl: false,
24927 enableAutocomplete: false,
24928 enableReverseGeocode: true,
24931 getAutoCreate: function()
24936 cls: 'roo-location-picker'
24942 initEvents: function(ct, position)
24944 if(!this.el.getWidth() || this.isApplied()){
24948 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24953 initial: function()
24955 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24956 this.fireEvent('loadexception', this);
24960 if(!this.mapTypeId){
24961 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24964 this.gMapContext = this.GMapContext();
24966 this.initOverlayView();
24968 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24972 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24973 _this.setPosition(_this.gMapContext.marker.position);
24976 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24977 _this.fireEvent('mapClick', this, event);
24981 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24982 _this.fireEvent('mapRightClick', this, event);
24986 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24987 _this.fireEvent('markerClick', this, event);
24991 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24992 _this.fireEvent('markerRightClick', this, event);
24996 this.setPosition(this.gMapContext.location);
24998 this.fireEvent('initial', this, this.gMapContext.location);
25001 initOverlayView: function()
25005 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25009 _this.fireEvent('OverlayViewDraw', _this);
25014 _this.fireEvent('OverlayViewOnAdd', _this);
25017 onRemove: function()
25019 _this.fireEvent('OverlayViewOnRemove', _this);
25022 show: function(cpx)
25024 _this.fireEvent('OverlayViewShow', _this, cpx);
25029 _this.fireEvent('OverlayViewHide', _this);
25035 fromLatLngToContainerPixel: function(event)
25037 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25040 isApplied: function()
25042 return this.getGmapContext() == false ? false : true;
25045 getGmapContext: function()
25047 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25050 GMapContext: function()
25052 var position = new google.maps.LatLng(this.latitude, this.longitude);
25054 var _map = new google.maps.Map(this.el.dom, {
25057 mapTypeId: this.mapTypeId,
25058 mapTypeControl: this.mapTypeControl,
25059 disableDoubleClickZoom: this.disableDoubleClickZoom,
25060 scrollwheel: this.scrollwheel,
25061 streetViewControl: this.streetViewControl,
25062 locationName: this.locationName,
25063 draggable: this.draggable,
25064 enableAutocomplete: this.enableAutocomplete,
25065 enableReverseGeocode: this.enableReverseGeocode
25068 var _marker = new google.maps.Marker({
25069 position: position,
25071 title: this.markerTitle,
25072 draggable: this.draggable
25079 location: position,
25080 radius: this.radius,
25081 locationName: this.locationName,
25082 addressComponents: {
25083 formatted_address: null,
25084 addressLine1: null,
25085 addressLine2: null,
25087 streetNumber: null,
25091 stateOrProvince: null
25094 domContainer: this.el.dom,
25095 geodecoder: new google.maps.Geocoder()
25099 drawCircle: function(center, radius, options)
25101 if (this.gMapContext.circle != null) {
25102 this.gMapContext.circle.setMap(null);
25106 options = Roo.apply({}, options, {
25107 strokeColor: "#0000FF",
25108 strokeOpacity: .35,
25110 fillColor: "#0000FF",
25114 options.map = this.gMapContext.map;
25115 options.radius = radius;
25116 options.center = center;
25117 this.gMapContext.circle = new google.maps.Circle(options);
25118 return this.gMapContext.circle;
25124 setPosition: function(location)
25126 this.gMapContext.location = location;
25127 this.gMapContext.marker.setPosition(location);
25128 this.gMapContext.map.panTo(location);
25129 this.drawCircle(location, this.gMapContext.radius, {});
25133 if (this.gMapContext.settings.enableReverseGeocode) {
25134 this.gMapContext.geodecoder.geocode({
25135 latLng: this.gMapContext.location
25136 }, function(results, status) {
25138 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25139 _this.gMapContext.locationName = results[0].formatted_address;
25140 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25142 _this.fireEvent('positionchanged', this, location);
25149 this.fireEvent('positionchanged', this, location);
25154 google.maps.event.trigger(this.gMapContext.map, "resize");
25156 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25158 this.fireEvent('resize', this);
25161 setPositionByLatLng: function(latitude, longitude)
25163 this.setPosition(new google.maps.LatLng(latitude, longitude));
25166 getCurrentPosition: function()
25169 latitude: this.gMapContext.location.lat(),
25170 longitude: this.gMapContext.location.lng()
25174 getAddressName: function()
25176 return this.gMapContext.locationName;
25179 getAddressComponents: function()
25181 return this.gMapContext.addressComponents;
25184 address_component_from_google_geocode: function(address_components)
25188 for (var i = 0; i < address_components.length; i++) {
25189 var component = address_components[i];
25190 if (component.types.indexOf("postal_code") >= 0) {
25191 result.postalCode = component.short_name;
25192 } else if (component.types.indexOf("street_number") >= 0) {
25193 result.streetNumber = component.short_name;
25194 } else if (component.types.indexOf("route") >= 0) {
25195 result.streetName = component.short_name;
25196 } else if (component.types.indexOf("neighborhood") >= 0) {
25197 result.city = component.short_name;
25198 } else if (component.types.indexOf("locality") >= 0) {
25199 result.city = component.short_name;
25200 } else if (component.types.indexOf("sublocality") >= 0) {
25201 result.district = component.short_name;
25202 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25203 result.stateOrProvince = component.short_name;
25204 } else if (component.types.indexOf("country") >= 0) {
25205 result.country = component.short_name;
25209 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25210 result.addressLine2 = "";
25214 setZoomLevel: function(zoom)
25216 this.gMapContext.map.setZoom(zoom);
25229 this.fireEvent('show', this);
25240 this.fireEvent('hide', this);
25245 Roo.apply(Roo.bootstrap.LocationPicker, {
25247 OverlayView : function(map, options)
25249 options = options || {};
25263 * @class Roo.bootstrap.Alert
25264 * @extends Roo.bootstrap.Component
25265 * Bootstrap Alert class
25266 * @cfg {String} title The title of alert
25267 * @cfg {String} html The content of alert
25268 * @cfg {String} weight ( success | info | warning | danger )
25269 * @cfg {String} faicon font-awesomeicon
25272 * Create a new alert
25273 * @param {Object} config The config object
25277 Roo.bootstrap.Alert = function(config){
25278 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25282 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
25289 getAutoCreate : function()
25298 cls : 'roo-alert-icon'
25303 cls : 'roo-alert-title',
25308 cls : 'roo-alert-text',
25315 cfg.cn[0].cls += ' fa ' + this.faicon;
25319 cfg.cls += ' alert-' + this.weight;
25325 initEvents: function()
25327 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25330 setTitle : function(str)
25332 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25335 setText : function(str)
25337 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25340 setWeight : function(weight)
25343 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25346 this.weight = weight;
25348 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25351 setIcon : function(icon)
25354 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25357 this.faicon = icon;
25359 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25380 * @class Roo.bootstrap.UploadCropbox
25381 * @extends Roo.bootstrap.Component
25382 * Bootstrap UploadCropbox class
25383 * @cfg {String} emptyText show when image has been loaded
25384 * @cfg {String} rotateNotify show when image too small to rotate
25385 * @cfg {Number} errorTimeout default 3000
25386 * @cfg {Number} minWidth default 300
25387 * @cfg {Number} minHeight default 300
25388 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25389 * @cfg {Boolean} isDocument (true|false) default false
25390 * @cfg {String} url action url
25391 * @cfg {String} paramName default 'imageUpload'
25392 * @cfg {String} method default POST
25393 * @cfg {Boolean} loadMask (true|false) default true
25394 * @cfg {Boolean} loadingText default 'Loading...'
25397 * Create a new UploadCropbox
25398 * @param {Object} config The config object
25401 Roo.bootstrap.UploadCropbox = function(config){
25402 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25406 * @event beforeselectfile
25407 * Fire before select file
25408 * @param {Roo.bootstrap.UploadCropbox} this
25410 "beforeselectfile" : true,
25413 * Fire after initEvent
25414 * @param {Roo.bootstrap.UploadCropbox} this
25419 * Fire after initEvent
25420 * @param {Roo.bootstrap.UploadCropbox} this
25421 * @param {String} data
25426 * Fire when preparing the file data
25427 * @param {Roo.bootstrap.UploadCropbox} this
25428 * @param {Object} file
25433 * Fire when get exception
25434 * @param {Roo.bootstrap.UploadCropbox} this
25435 * @param {XMLHttpRequest} xhr
25437 "exception" : true,
25439 * @event beforeloadcanvas
25440 * Fire before load the canvas
25441 * @param {Roo.bootstrap.UploadCropbox} this
25442 * @param {String} src
25444 "beforeloadcanvas" : true,
25447 * Fire when trash image
25448 * @param {Roo.bootstrap.UploadCropbox} this
25453 * Fire when download the image
25454 * @param {Roo.bootstrap.UploadCropbox} this
25458 * @event footerbuttonclick
25459 * Fire when footerbuttonclick
25460 * @param {Roo.bootstrap.UploadCropbox} this
25461 * @param {String} type
25463 "footerbuttonclick" : true,
25467 * @param {Roo.bootstrap.UploadCropbox} this
25472 * Fire when rotate the image
25473 * @param {Roo.bootstrap.UploadCropbox} this
25474 * @param {String} pos
25479 * Fire when inspect the file
25480 * @param {Roo.bootstrap.UploadCropbox} this
25481 * @param {Object} file
25486 * Fire when xhr upload the file
25487 * @param {Roo.bootstrap.UploadCropbox} this
25488 * @param {Object} data
25493 * Fire when arrange the file data
25494 * @param {Roo.bootstrap.UploadCropbox} this
25495 * @param {Object} formData
25500 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25503 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25505 emptyText : 'Click to upload image',
25506 rotateNotify : 'Image is too small to rotate',
25507 errorTimeout : 3000,
25521 cropType : 'image/jpeg',
25523 canvasLoaded : false,
25524 isDocument : false,
25526 paramName : 'imageUpload',
25528 loadingText : 'Loading...',
25531 getAutoCreate : function()
25535 cls : 'roo-upload-cropbox',
25539 cls : 'roo-upload-cropbox-selector',
25544 cls : 'roo-upload-cropbox-body',
25545 style : 'cursor:pointer',
25549 cls : 'roo-upload-cropbox-preview'
25553 cls : 'roo-upload-cropbox-thumb'
25557 cls : 'roo-upload-cropbox-empty-notify',
25558 html : this.emptyText
25562 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25563 html : this.rotateNotify
25569 cls : 'roo-upload-cropbox-footer',
25572 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25582 onRender : function(ct, position)
25584 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25586 if (this.buttons.length) {
25588 Roo.each(this.buttons, function(bb) {
25590 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25592 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25598 this.maskEl = this.el;
25602 initEvents : function()
25604 this.urlAPI = (window.createObjectURL && window) ||
25605 (window.URL && URL.revokeObjectURL && URL) ||
25606 (window.webkitURL && webkitURL);
25608 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25609 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25611 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25612 this.selectorEl.hide();
25614 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25615 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25617 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25618 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25619 this.thumbEl.hide();
25621 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25622 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25624 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25625 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25626 this.errorEl.hide();
25628 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25629 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25630 this.footerEl.hide();
25632 this.setThumbBoxSize();
25638 this.fireEvent('initial', this);
25645 window.addEventListener("resize", function() { _this.resize(); } );
25647 this.bodyEl.on('click', this.beforeSelectFile, this);
25650 this.bodyEl.on('touchstart', this.onTouchStart, this);
25651 this.bodyEl.on('touchmove', this.onTouchMove, this);
25652 this.bodyEl.on('touchend', this.onTouchEnd, this);
25656 this.bodyEl.on('mousedown', this.onMouseDown, this);
25657 this.bodyEl.on('mousemove', this.onMouseMove, this);
25658 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25659 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25660 Roo.get(document).on('mouseup', this.onMouseUp, this);
25663 this.selectorEl.on('change', this.onFileSelected, this);
25669 this.baseScale = 1;
25671 this.baseRotate = 1;
25672 this.dragable = false;
25673 this.pinching = false;
25676 this.cropData = false;
25677 this.notifyEl.dom.innerHTML = this.emptyText;
25679 this.selectorEl.dom.value = '';
25683 resize : function()
25685 if(this.fireEvent('resize', this) != false){
25686 this.setThumbBoxPosition();
25687 this.setCanvasPosition();
25691 onFooterButtonClick : function(e, el, o, type)
25694 case 'rotate-left' :
25695 this.onRotateLeft(e);
25697 case 'rotate-right' :
25698 this.onRotateRight(e);
25701 this.beforeSelectFile(e);
25716 this.fireEvent('footerbuttonclick', this, type);
25719 beforeSelectFile : function(e)
25721 e.preventDefault();
25723 if(this.fireEvent('beforeselectfile', this) != false){
25724 this.selectorEl.dom.click();
25728 onFileSelected : function(e)
25730 e.preventDefault();
25732 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25736 var file = this.selectorEl.dom.files[0];
25738 if(this.fireEvent('inspect', this, file) != false){
25739 this.prepare(file);
25744 trash : function(e)
25746 this.fireEvent('trash', this);
25749 download : function(e)
25751 this.fireEvent('download', this);
25754 loadCanvas : function(src)
25756 if(this.fireEvent('beforeloadcanvas', this, src) != false){
25760 this.imageEl = document.createElement('img');
25764 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25766 this.imageEl.src = src;
25770 onLoadCanvas : function()
25772 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25773 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25775 this.bodyEl.un('click', this.beforeSelectFile, this);
25777 this.notifyEl.hide();
25778 this.thumbEl.show();
25779 this.footerEl.show();
25781 this.baseRotateLevel();
25783 if(this.isDocument){
25784 this.setThumbBoxSize();
25787 this.setThumbBoxPosition();
25789 this.baseScaleLevel();
25795 this.canvasLoaded = true;
25798 this.maskEl.unmask();
25803 setCanvasPosition : function()
25805 if(!this.canvasEl){
25809 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25810 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25812 this.previewEl.setLeft(pw);
25813 this.previewEl.setTop(ph);
25817 onMouseDown : function(e)
25821 this.dragable = true;
25822 this.pinching = false;
25824 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25825 this.dragable = false;
25829 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25830 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25834 onMouseMove : function(e)
25838 if(!this.canvasLoaded){
25842 if (!this.dragable){
25846 var minX = Math.ceil(this.thumbEl.getLeft(true));
25847 var minY = Math.ceil(this.thumbEl.getTop(true));
25849 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25850 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25852 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25853 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25855 x = x - this.mouseX;
25856 y = y - this.mouseY;
25858 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25859 var bgY = Math.ceil(y + this.previewEl.getTop(true));
25861 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25862 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25864 this.previewEl.setLeft(bgX);
25865 this.previewEl.setTop(bgY);
25867 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25868 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25871 onMouseUp : function(e)
25875 this.dragable = false;
25878 onMouseWheel : function(e)
25882 this.startScale = this.scale;
25884 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25886 if(!this.zoomable()){
25887 this.scale = this.startScale;
25896 zoomable : function()
25898 var minScale = this.thumbEl.getWidth() / this.minWidth;
25900 if(this.minWidth < this.minHeight){
25901 minScale = this.thumbEl.getHeight() / this.minHeight;
25904 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25905 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25909 (this.rotate == 0 || this.rotate == 180) &&
25911 width > this.imageEl.OriginWidth ||
25912 height > this.imageEl.OriginHeight ||
25913 (width < this.minWidth && height < this.minHeight)
25921 (this.rotate == 90 || this.rotate == 270) &&
25923 width > this.imageEl.OriginWidth ||
25924 height > this.imageEl.OriginHeight ||
25925 (width < this.minHeight && height < this.minWidth)
25932 !this.isDocument &&
25933 (this.rotate == 0 || this.rotate == 180) &&
25935 width < this.minWidth ||
25936 width > this.imageEl.OriginWidth ||
25937 height < this.minHeight ||
25938 height > this.imageEl.OriginHeight
25945 !this.isDocument &&
25946 (this.rotate == 90 || this.rotate == 270) &&
25948 width < this.minHeight ||
25949 width > this.imageEl.OriginWidth ||
25950 height < this.minWidth ||
25951 height > this.imageEl.OriginHeight
25961 onRotateLeft : function(e)
25963 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25965 var minScale = this.thumbEl.getWidth() / this.minWidth;
25967 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25968 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25970 this.startScale = this.scale;
25972 while (this.getScaleLevel() < minScale){
25974 this.scale = this.scale + 1;
25976 if(!this.zoomable()){
25981 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25982 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25987 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25994 this.scale = this.startScale;
25996 this.onRotateFail();
26001 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26003 if(this.isDocument){
26004 this.setThumbBoxSize();
26005 this.setThumbBoxPosition();
26006 this.setCanvasPosition();
26011 this.fireEvent('rotate', this, 'left');
26015 onRotateRight : function(e)
26017 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26019 var minScale = this.thumbEl.getWidth() / this.minWidth;
26021 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26022 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26024 this.startScale = this.scale;
26026 while (this.getScaleLevel() < minScale){
26028 this.scale = this.scale + 1;
26030 if(!this.zoomable()){
26035 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26036 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26041 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26048 this.scale = this.startScale;
26050 this.onRotateFail();
26055 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26057 if(this.isDocument){
26058 this.setThumbBoxSize();
26059 this.setThumbBoxPosition();
26060 this.setCanvasPosition();
26065 this.fireEvent('rotate', this, 'right');
26068 onRotateFail : function()
26070 this.errorEl.show(true);
26074 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26079 this.previewEl.dom.innerHTML = '';
26081 var canvasEl = document.createElement("canvas");
26083 var contextEl = canvasEl.getContext("2d");
26085 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26086 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26087 var center = this.imageEl.OriginWidth / 2;
26089 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26090 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26091 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26092 center = this.imageEl.OriginHeight / 2;
26095 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26097 contextEl.translate(center, center);
26098 contextEl.rotate(this.rotate * Math.PI / 180);
26100 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26102 this.canvasEl = document.createElement("canvas");
26104 this.contextEl = this.canvasEl.getContext("2d");
26106 switch (this.rotate) {
26109 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26110 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26112 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26117 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26118 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26120 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26121 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);
26125 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26130 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26131 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26133 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26134 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);
26138 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);
26143 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26144 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26146 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26147 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26151 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);
26158 this.previewEl.appendChild(this.canvasEl);
26160 this.setCanvasPosition();
26165 if(!this.canvasLoaded){
26169 var imageCanvas = document.createElement("canvas");
26171 var imageContext = imageCanvas.getContext("2d");
26173 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26174 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26176 var center = imageCanvas.width / 2;
26178 imageContext.translate(center, center);
26180 imageContext.rotate(this.rotate * Math.PI / 180);
26182 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26184 var canvas = document.createElement("canvas");
26186 var context = canvas.getContext("2d");
26188 canvas.width = this.minWidth;
26189 canvas.height = this.minHeight;
26191 switch (this.rotate) {
26194 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26195 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26197 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26198 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26200 var targetWidth = this.minWidth - 2 * x;
26201 var targetHeight = this.minHeight - 2 * y;
26205 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26206 scale = targetWidth / width;
26209 if(x > 0 && y == 0){
26210 scale = targetHeight / height;
26213 if(x > 0 && y > 0){
26214 scale = targetWidth / width;
26216 if(width < height){
26217 scale = targetHeight / height;
26221 context.scale(scale, scale);
26223 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26224 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26226 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26227 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26229 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26234 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26235 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26237 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26238 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26240 var targetWidth = this.minWidth - 2 * x;
26241 var targetHeight = this.minHeight - 2 * y;
26245 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26246 scale = targetWidth / width;
26249 if(x > 0 && y == 0){
26250 scale = targetHeight / height;
26253 if(x > 0 && y > 0){
26254 scale = targetWidth / width;
26256 if(width < height){
26257 scale = targetHeight / height;
26261 context.scale(scale, scale);
26263 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26264 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26266 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26267 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26269 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26271 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26276 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26277 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26279 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26280 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26282 var targetWidth = this.minWidth - 2 * x;
26283 var targetHeight = this.minHeight - 2 * y;
26287 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26288 scale = targetWidth / width;
26291 if(x > 0 && y == 0){
26292 scale = targetHeight / height;
26295 if(x > 0 && y > 0){
26296 scale = targetWidth / width;
26298 if(width < height){
26299 scale = targetHeight / height;
26303 context.scale(scale, scale);
26305 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26306 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26308 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26309 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26311 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26312 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26314 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26319 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26320 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26322 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26323 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26325 var targetWidth = this.minWidth - 2 * x;
26326 var targetHeight = this.minHeight - 2 * y;
26330 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26331 scale = targetWidth / width;
26334 if(x > 0 && y == 0){
26335 scale = targetHeight / height;
26338 if(x > 0 && y > 0){
26339 scale = targetWidth / width;
26341 if(width < height){
26342 scale = targetHeight / height;
26346 context.scale(scale, scale);
26348 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26349 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26351 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26352 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26354 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26356 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26363 this.cropData = canvas.toDataURL(this.cropType);
26365 if(this.fireEvent('crop', this, this.cropData) !== false){
26366 this.process(this.file, this.cropData);
26373 setThumbBoxSize : function()
26377 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26378 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26379 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26381 this.minWidth = width;
26382 this.minHeight = height;
26384 if(this.rotate == 90 || this.rotate == 270){
26385 this.minWidth = height;
26386 this.minHeight = width;
26391 width = Math.ceil(this.minWidth * height / this.minHeight);
26393 if(this.minWidth > this.minHeight){
26395 height = Math.ceil(this.minHeight * width / this.minWidth);
26398 this.thumbEl.setStyle({
26399 width : width + 'px',
26400 height : height + 'px'
26407 setThumbBoxPosition : function()
26409 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26410 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26412 this.thumbEl.setLeft(x);
26413 this.thumbEl.setTop(y);
26417 baseRotateLevel : function()
26419 this.baseRotate = 1;
26422 typeof(this.exif) != 'undefined' &&
26423 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26424 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26426 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26429 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26433 baseScaleLevel : function()
26437 if(this.isDocument){
26439 if(this.baseRotate == 6 || this.baseRotate == 8){
26441 height = this.thumbEl.getHeight();
26442 this.baseScale = height / this.imageEl.OriginWidth;
26444 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26445 width = this.thumbEl.getWidth();
26446 this.baseScale = width / this.imageEl.OriginHeight;
26452 height = this.thumbEl.getHeight();
26453 this.baseScale = height / this.imageEl.OriginHeight;
26455 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26456 width = this.thumbEl.getWidth();
26457 this.baseScale = width / this.imageEl.OriginWidth;
26463 if(this.baseRotate == 6 || this.baseRotate == 8){
26465 width = this.thumbEl.getHeight();
26466 this.baseScale = width / this.imageEl.OriginHeight;
26468 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26469 height = this.thumbEl.getWidth();
26470 this.baseScale = height / this.imageEl.OriginHeight;
26473 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26474 height = this.thumbEl.getWidth();
26475 this.baseScale = height / this.imageEl.OriginHeight;
26477 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26478 width = this.thumbEl.getHeight();
26479 this.baseScale = width / this.imageEl.OriginWidth;
26486 width = this.thumbEl.getWidth();
26487 this.baseScale = width / this.imageEl.OriginWidth;
26489 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26490 height = this.thumbEl.getHeight();
26491 this.baseScale = height / this.imageEl.OriginHeight;
26494 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26496 height = this.thumbEl.getHeight();
26497 this.baseScale = height / this.imageEl.OriginHeight;
26499 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26500 width = this.thumbEl.getWidth();
26501 this.baseScale = width / this.imageEl.OriginWidth;
26509 getScaleLevel : function()
26511 return this.baseScale * Math.pow(1.1, this.scale);
26514 onTouchStart : function(e)
26516 if(!this.canvasLoaded){
26517 this.beforeSelectFile(e);
26521 var touches = e.browserEvent.touches;
26527 if(touches.length == 1){
26528 this.onMouseDown(e);
26532 if(touches.length != 2){
26538 for(var i = 0, finger; finger = touches[i]; i++){
26539 coords.push(finger.pageX, finger.pageY);
26542 var x = Math.pow(coords[0] - coords[2], 2);
26543 var y = Math.pow(coords[1] - coords[3], 2);
26545 this.startDistance = Math.sqrt(x + y);
26547 this.startScale = this.scale;
26549 this.pinching = true;
26550 this.dragable = false;
26554 onTouchMove : function(e)
26556 if(!this.pinching && !this.dragable){
26560 var touches = e.browserEvent.touches;
26567 this.onMouseMove(e);
26573 for(var i = 0, finger; finger = touches[i]; i++){
26574 coords.push(finger.pageX, finger.pageY);
26577 var x = Math.pow(coords[0] - coords[2], 2);
26578 var y = Math.pow(coords[1] - coords[3], 2);
26580 this.endDistance = Math.sqrt(x + y);
26582 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26584 if(!this.zoomable()){
26585 this.scale = this.startScale;
26593 onTouchEnd : function(e)
26595 this.pinching = false;
26596 this.dragable = false;
26600 process : function(file, crop)
26603 this.maskEl.mask(this.loadingText);
26606 this.xhr = new XMLHttpRequest();
26608 file.xhr = this.xhr;
26610 this.xhr.open(this.method, this.url, true);
26613 "Accept": "application/json",
26614 "Cache-Control": "no-cache",
26615 "X-Requested-With": "XMLHttpRequest"
26618 for (var headerName in headers) {
26619 var headerValue = headers[headerName];
26621 this.xhr.setRequestHeader(headerName, headerValue);
26627 this.xhr.onload = function()
26629 _this.xhrOnLoad(_this.xhr);
26632 this.xhr.onerror = function()
26634 _this.xhrOnError(_this.xhr);
26637 var formData = new FormData();
26639 formData.append('returnHTML', 'NO');
26642 formData.append('crop', crop);
26645 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26646 formData.append(this.paramName, file, file.name);
26649 if(typeof(file.filename) != 'undefined'){
26650 formData.append('filename', file.filename);
26653 if(typeof(file.mimetype) != 'undefined'){
26654 formData.append('mimetype', file.mimetype);
26657 if(this.fireEvent('arrange', this, formData) != false){
26658 this.xhr.send(formData);
26662 xhrOnLoad : function(xhr)
26665 this.maskEl.unmask();
26668 if (xhr.readyState !== 4) {
26669 this.fireEvent('exception', this, xhr);
26673 var response = Roo.decode(xhr.responseText);
26675 if(!response.success){
26676 this.fireEvent('exception', this, xhr);
26680 var response = Roo.decode(xhr.responseText);
26682 this.fireEvent('upload', this, response);
26686 xhrOnError : function()
26689 this.maskEl.unmask();
26692 Roo.log('xhr on error');
26694 var response = Roo.decode(xhr.responseText);
26700 prepare : function(file)
26703 this.maskEl.mask(this.loadingText);
26709 if(typeof(file) === 'string'){
26710 this.loadCanvas(file);
26714 if(!file || !this.urlAPI){
26719 this.cropType = file.type;
26723 if(this.fireEvent('prepare', this, this.file) != false){
26725 var reader = new FileReader();
26727 reader.onload = function (e) {
26728 if (e.target.error) {
26729 Roo.log(e.target.error);
26733 var buffer = e.target.result,
26734 dataView = new DataView(buffer),
26736 maxOffset = dataView.byteLength - 4,
26740 if (dataView.getUint16(0) === 0xffd8) {
26741 while (offset < maxOffset) {
26742 markerBytes = dataView.getUint16(offset);
26744 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26745 markerLength = dataView.getUint16(offset + 2) + 2;
26746 if (offset + markerLength > dataView.byteLength) {
26747 Roo.log('Invalid meta data: Invalid segment size.');
26751 if(markerBytes == 0xffe1){
26752 _this.parseExifData(
26759 offset += markerLength;
26769 var url = _this.urlAPI.createObjectURL(_this.file);
26771 _this.loadCanvas(url);
26776 reader.readAsArrayBuffer(this.file);
26782 parseExifData : function(dataView, offset, length)
26784 var tiffOffset = offset + 10,
26788 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26789 // No Exif data, might be XMP data instead
26793 // Check for the ASCII code for "Exif" (0x45786966):
26794 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26795 // No Exif data, might be XMP data instead
26798 if (tiffOffset + 8 > dataView.byteLength) {
26799 Roo.log('Invalid Exif data: Invalid segment size.');
26802 // Check for the two null bytes:
26803 if (dataView.getUint16(offset + 8) !== 0x0000) {
26804 Roo.log('Invalid Exif data: Missing byte alignment offset.');
26807 // Check the byte alignment:
26808 switch (dataView.getUint16(tiffOffset)) {
26810 littleEndian = true;
26813 littleEndian = false;
26816 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26819 // Check for the TIFF tag marker (0x002A):
26820 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26821 Roo.log('Invalid Exif data: Missing TIFF marker.');
26824 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26825 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26827 this.parseExifTags(
26830 tiffOffset + dirOffset,
26835 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26840 if (dirOffset + 6 > dataView.byteLength) {
26841 Roo.log('Invalid Exif data: Invalid directory offset.');
26844 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26845 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26846 if (dirEndOffset + 4 > dataView.byteLength) {
26847 Roo.log('Invalid Exif data: Invalid directory size.');
26850 for (i = 0; i < tagsNumber; i += 1) {
26854 dirOffset + 2 + 12 * i, // tag offset
26858 // Return the offset to the next directory:
26859 return dataView.getUint32(dirEndOffset, littleEndian);
26862 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
26864 var tag = dataView.getUint16(offset, littleEndian);
26866 this.exif[tag] = this.getExifValue(
26870 dataView.getUint16(offset + 2, littleEndian), // tag type
26871 dataView.getUint32(offset + 4, littleEndian), // tag length
26876 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26878 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26887 Roo.log('Invalid Exif data: Invalid tag type.');
26891 tagSize = tagType.size * length;
26892 // Determine if the value is contained in the dataOffset bytes,
26893 // or if the value at the dataOffset is a pointer to the actual data:
26894 dataOffset = tagSize > 4 ?
26895 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26896 if (dataOffset + tagSize > dataView.byteLength) {
26897 Roo.log('Invalid Exif data: Invalid data offset.');
26900 if (length === 1) {
26901 return tagType.getValue(dataView, dataOffset, littleEndian);
26904 for (i = 0; i < length; i += 1) {
26905 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26908 if (tagType.ascii) {
26910 // Concatenate the chars:
26911 for (i = 0; i < values.length; i += 1) {
26913 // Ignore the terminating NULL byte(s):
26914 if (c === '\u0000') {
26926 Roo.apply(Roo.bootstrap.UploadCropbox, {
26928 'Orientation': 0x0112
26932 1: 0, //'top-left',
26934 3: 180, //'bottom-right',
26935 // 4: 'bottom-left',
26937 6: 90, //'right-top',
26938 // 7: 'right-bottom',
26939 8: 270 //'left-bottom'
26943 // byte, 8-bit unsigned int:
26945 getValue: function (dataView, dataOffset) {
26946 return dataView.getUint8(dataOffset);
26950 // ascii, 8-bit byte:
26952 getValue: function (dataView, dataOffset) {
26953 return String.fromCharCode(dataView.getUint8(dataOffset));
26958 // short, 16 bit int:
26960 getValue: function (dataView, dataOffset, littleEndian) {
26961 return dataView.getUint16(dataOffset, littleEndian);
26965 // long, 32 bit int:
26967 getValue: function (dataView, dataOffset, littleEndian) {
26968 return dataView.getUint32(dataOffset, littleEndian);
26972 // rational = two long values, first is numerator, second is denominator:
26974 getValue: function (dataView, dataOffset, littleEndian) {
26975 return dataView.getUint32(dataOffset, littleEndian) /
26976 dataView.getUint32(dataOffset + 4, littleEndian);
26980 // slong, 32 bit signed int:
26982 getValue: function (dataView, dataOffset, littleEndian) {
26983 return dataView.getInt32(dataOffset, littleEndian);
26987 // srational, two slongs, first is numerator, second is denominator:
26989 getValue: function (dataView, dataOffset, littleEndian) {
26990 return dataView.getInt32(dataOffset, littleEndian) /
26991 dataView.getInt32(dataOffset + 4, littleEndian);
27001 cls : 'btn-group roo-upload-cropbox-rotate-left',
27002 action : 'rotate-left',
27006 cls : 'btn btn-default',
27007 html : '<i class="fa fa-undo"></i>'
27013 cls : 'btn-group roo-upload-cropbox-picture',
27014 action : 'picture',
27018 cls : 'btn btn-default',
27019 html : '<i class="fa fa-picture-o"></i>'
27025 cls : 'btn-group roo-upload-cropbox-rotate-right',
27026 action : 'rotate-right',
27030 cls : 'btn btn-default',
27031 html : '<i class="fa fa-repeat"></i>'
27039 cls : 'btn-group roo-upload-cropbox-rotate-left',
27040 action : 'rotate-left',
27044 cls : 'btn btn-default',
27045 html : '<i class="fa fa-undo"></i>'
27051 cls : 'btn-group roo-upload-cropbox-download',
27052 action : 'download',
27056 cls : 'btn btn-default',
27057 html : '<i class="fa fa-download"></i>'
27063 cls : 'btn-group roo-upload-cropbox-crop',
27068 cls : 'btn btn-default',
27069 html : '<i class="fa fa-crop"></i>'
27075 cls : 'btn-group roo-upload-cropbox-trash',
27080 cls : 'btn btn-default',
27081 html : '<i class="fa fa-trash"></i>'
27087 cls : 'btn-group roo-upload-cropbox-rotate-right',
27088 action : 'rotate-right',
27092 cls : 'btn btn-default',
27093 html : '<i class="fa fa-repeat"></i>'
27101 cls : 'btn-group roo-upload-cropbox-rotate-left',
27102 action : 'rotate-left',
27106 cls : 'btn btn-default',
27107 html : '<i class="fa fa-undo"></i>'
27113 cls : 'btn-group roo-upload-cropbox-rotate-right',
27114 action : 'rotate-right',
27118 cls : 'btn btn-default',
27119 html : '<i class="fa fa-repeat"></i>'
27132 * @class Roo.bootstrap.DocumentManager
27133 * @extends Roo.bootstrap.Component
27134 * Bootstrap DocumentManager class
27135 * @cfg {String} paramName default 'imageUpload'
27136 * @cfg {String} method default POST
27137 * @cfg {String} url action url
27138 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27139 * @cfg {Boolean} multiple multiple upload default true
27140 * @cfg {Number} thumbSize default 300
27141 * @cfg {String} fieldLabel
27142 * @cfg {Number} labelWidth default 4
27143 * @cfg {String} labelAlign (left|top) default left
27144 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27147 * Create a new DocumentManager
27148 * @param {Object} config The config object
27151 Roo.bootstrap.DocumentManager = function(config){
27152 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27157 * Fire when initial the DocumentManager
27158 * @param {Roo.bootstrap.DocumentManager} this
27163 * inspect selected file
27164 * @param {Roo.bootstrap.DocumentManager} this
27165 * @param {File} file
27170 * Fire when xhr load exception
27171 * @param {Roo.bootstrap.DocumentManager} this
27172 * @param {XMLHttpRequest} xhr
27174 "exception" : true,
27177 * prepare the form data
27178 * @param {Roo.bootstrap.DocumentManager} this
27179 * @param {Object} formData
27184 * Fire when remove the file
27185 * @param {Roo.bootstrap.DocumentManager} this
27186 * @param {Object} file
27191 * Fire after refresh the file
27192 * @param {Roo.bootstrap.DocumentManager} this
27197 * Fire after click the image
27198 * @param {Roo.bootstrap.DocumentManager} this
27199 * @param {Object} file
27204 * Fire when upload a image and editable set to true
27205 * @param {Roo.bootstrap.DocumentManager} this
27206 * @param {Object} file
27210 * @event beforeselectfile
27211 * Fire before select file
27212 * @param {Roo.bootstrap.DocumentManager} this
27214 "beforeselectfile" : true,
27217 * Fire before process file
27218 * @param {Roo.bootstrap.DocumentManager} this
27219 * @param {Object} file
27226 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
27235 paramName : 'imageUpload',
27238 labelAlign : 'left',
27245 getAutoCreate : function()
27247 var managerWidget = {
27249 cls : 'roo-document-manager',
27253 cls : 'roo-document-manager-selector',
27258 cls : 'roo-document-manager-uploader',
27262 cls : 'roo-document-manager-upload-btn',
27263 html : '<i class="fa fa-plus"></i>'
27274 cls : 'column col-md-12',
27279 if(this.fieldLabel.length){
27284 cls : 'column col-md-12',
27285 html : this.fieldLabel
27289 cls : 'column col-md-12',
27294 if(this.labelAlign == 'left'){
27298 cls : 'column col-md-' + this.labelWidth,
27299 html : this.fieldLabel
27303 cls : 'column col-md-' + (12 - this.labelWidth),
27313 cls : 'row clearfix',
27321 initEvents : function()
27323 this.managerEl = this.el.select('.roo-document-manager', true).first();
27324 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27326 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27327 this.selectorEl.hide();
27330 this.selectorEl.attr('multiple', 'multiple');
27333 this.selectorEl.on('change', this.onFileSelected, this);
27335 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27336 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27338 this.uploader.on('click', this.onUploaderClick, this);
27340 this.renderProgressDialog();
27344 window.addEventListener("resize", function() { _this.refresh(); } );
27346 this.fireEvent('initial', this);
27349 renderProgressDialog : function()
27353 this.progressDialog = new Roo.bootstrap.Modal({
27354 cls : 'roo-document-manager-progress-dialog',
27355 allow_close : false,
27365 btnclick : function() {
27366 _this.uploadCancel();
27372 this.progressDialog.render(Roo.get(document.body));
27374 this.progress = new Roo.bootstrap.Progress({
27375 cls : 'roo-document-manager-progress',
27380 this.progress.render(this.progressDialog.getChildContainer());
27382 this.progressBar = new Roo.bootstrap.ProgressBar({
27383 cls : 'roo-document-manager-progress-bar',
27386 aria_valuemax : 12,
27390 this.progressBar.render(this.progress.getChildContainer());
27393 onUploaderClick : function(e)
27395 e.preventDefault();
27397 if(this.fireEvent('beforeselectfile', this) != false){
27398 this.selectorEl.dom.click();
27403 onFileSelected : function(e)
27405 e.preventDefault();
27407 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27411 Roo.each(this.selectorEl.dom.files, function(file){
27412 if(this.fireEvent('inspect', this, file) != false){
27413 this.files.push(file);
27423 this.selectorEl.dom.value = '';
27425 if(!this.files.length){
27429 if(this.boxes > 0 && this.files.length > this.boxes){
27430 this.files = this.files.slice(0, this.boxes);
27433 this.uploader.show();
27435 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27436 this.uploader.hide();
27445 Roo.each(this.files, function(file){
27447 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27448 var f = this.renderPreview(file);
27453 if(file.type.indexOf('image') != -1){
27454 this.delegates.push(
27456 _this.process(file);
27457 }).createDelegate(this)
27465 _this.process(file);
27466 }).createDelegate(this)
27471 this.files = files;
27473 this.delegates = this.delegates.concat(docs);
27475 if(!this.delegates.length){
27480 this.progressBar.aria_valuemax = this.delegates.length;
27487 arrange : function()
27489 if(!this.delegates.length){
27490 this.progressDialog.hide();
27495 var delegate = this.delegates.shift();
27497 this.progressDialog.show();
27499 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27501 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27506 refresh : function()
27508 this.uploader.show();
27510 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27511 this.uploader.hide();
27514 Roo.isTouch ? this.closable(false) : this.closable(true);
27516 this.fireEvent('refresh', this);
27519 onRemove : function(e, el, o)
27521 e.preventDefault();
27523 this.fireEvent('remove', this, o);
27527 remove : function(o)
27531 Roo.each(this.files, function(file){
27532 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27541 this.files = files;
27548 Roo.each(this.files, function(file){
27553 file.target.remove();
27562 onClick : function(e, el, o)
27564 e.preventDefault();
27566 this.fireEvent('click', this, o);
27570 closable : function(closable)
27572 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27574 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27586 xhrOnLoad : function(xhr)
27588 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27592 if (xhr.readyState !== 4) {
27594 this.fireEvent('exception', this, xhr);
27598 var response = Roo.decode(xhr.responseText);
27600 if(!response.success){
27602 this.fireEvent('exception', this, xhr);
27606 var file = this.renderPreview(response.data);
27608 this.files.push(file);
27614 xhrOnError : function(xhr)
27616 Roo.log('xhr on error');
27618 var response = Roo.decode(xhr.responseText);
27625 process : function(file)
27627 if(this.fireEvent('process', this, file) !== false){
27628 if(this.editable && file.type.indexOf('image') != -1){
27629 this.fireEvent('edit', this, file);
27633 this.uploadStart(file, false);
27640 uploadStart : function(file, crop)
27642 this.xhr = new XMLHttpRequest();
27644 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27649 file.xhr = this.xhr;
27651 this.managerEl.createChild({
27653 cls : 'roo-document-manager-loading',
27657 tooltip : file.name,
27658 cls : 'roo-document-manager-thumb',
27659 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27665 this.xhr.open(this.method, this.url, true);
27668 "Accept": "application/json",
27669 "Cache-Control": "no-cache",
27670 "X-Requested-With": "XMLHttpRequest"
27673 for (var headerName in headers) {
27674 var headerValue = headers[headerName];
27676 this.xhr.setRequestHeader(headerName, headerValue);
27682 this.xhr.onload = function()
27684 _this.xhrOnLoad(_this.xhr);
27687 this.xhr.onerror = function()
27689 _this.xhrOnError(_this.xhr);
27692 var formData = new FormData();
27694 formData.append('returnHTML', 'NO');
27697 formData.append('crop', crop);
27700 formData.append(this.paramName, file, file.name);
27702 if(this.fireEvent('prepare', this, formData) != false){
27703 this.xhr.send(formData);
27707 uploadCancel : function()
27714 this.delegates = [];
27716 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27723 renderPreview : function(file)
27725 if(typeof(file.target) != 'undefined' && file.target){
27729 var previewEl = this.managerEl.createChild({
27731 cls : 'roo-document-manager-preview',
27735 tooltip : file.filename,
27736 cls : 'roo-document-manager-thumb',
27737 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27742 html : '<i class="fa fa-times-circle"></i>'
27747 var close = previewEl.select('button.close', true).first();
27749 close.on('click', this.onRemove, this, file);
27751 file.target = previewEl;
27753 var image = previewEl.select('img', true).first();
27757 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27759 image.on('click', this.onClick, this, file);
27765 onPreviewLoad : function(file, image)
27767 if(typeof(file.target) == 'undefined' || !file.target){
27771 var width = image.dom.naturalWidth || image.dom.width;
27772 var height = image.dom.naturalHeight || image.dom.height;
27774 if(width > height){
27775 file.target.addClass('wide');
27779 file.target.addClass('tall');
27784 uploadFromSource : function(file, crop)
27786 this.xhr = new XMLHttpRequest();
27788 this.managerEl.createChild({
27790 cls : 'roo-document-manager-loading',
27794 tooltip : file.name,
27795 cls : 'roo-document-manager-thumb',
27796 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27802 this.xhr.open(this.method, this.url, true);
27805 "Accept": "application/json",
27806 "Cache-Control": "no-cache",
27807 "X-Requested-With": "XMLHttpRequest"
27810 for (var headerName in headers) {
27811 var headerValue = headers[headerName];
27813 this.xhr.setRequestHeader(headerName, headerValue);
27819 this.xhr.onload = function()
27821 _this.xhrOnLoad(_this.xhr);
27824 this.xhr.onerror = function()
27826 _this.xhrOnError(_this.xhr);
27829 var formData = new FormData();
27831 formData.append('returnHTML', 'NO');
27833 formData.append('crop', crop);
27835 if(typeof(file.filename) != 'undefined'){
27836 formData.append('filename', file.filename);
27839 if(typeof(file.mimetype) != 'undefined'){
27840 formData.append('mimetype', file.mimetype);
27843 if(this.fireEvent('prepare', this, formData) != false){
27844 this.xhr.send(formData);
27854 * @class Roo.bootstrap.DocumentViewer
27855 * @extends Roo.bootstrap.Component
27856 * Bootstrap DocumentViewer class
27859 * Create a new DocumentViewer
27860 * @param {Object} config The config object
27863 Roo.bootstrap.DocumentViewer = function(config){
27864 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27869 * Fire after initEvent
27870 * @param {Roo.bootstrap.DocumentViewer} this
27876 * @param {Roo.bootstrap.DocumentViewer} this
27881 * Fire after trash button
27882 * @param {Roo.bootstrap.DocumentViewer} this
27889 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27891 getAutoCreate : function()
27895 cls : 'roo-document-viewer',
27899 cls : 'roo-document-viewer-body',
27903 cls : 'roo-document-viewer-thumb',
27907 cls : 'roo-document-viewer-image'
27915 cls : 'roo-document-viewer-footer',
27918 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27926 cls : 'btn btn-default roo-document-viewer-trash',
27927 html : '<i class="fa fa-trash"></i>'
27940 initEvents : function()
27943 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27944 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27946 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27947 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27949 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27950 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27952 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27953 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27955 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27956 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27958 this.bodyEl.on('click', this.onClick, this);
27960 this.trashBtn.on('click', this.onTrash, this);
27964 initial : function()
27966 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27969 this.fireEvent('initial', this);
27973 onClick : function(e)
27975 e.preventDefault();
27977 this.fireEvent('click', this);
27980 onTrash : function(e)
27982 e.preventDefault();
27984 this.fireEvent('trash', this);
27996 * @class Roo.bootstrap.NavProgressBar
27997 * @extends Roo.bootstrap.Component
27998 * Bootstrap NavProgressBar class
28001 * Create a new nav progress bar
28002 * @param {Object} config The config object
28005 Roo.bootstrap.NavProgressBar = function(config){
28006 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28008 this.bullets = this.bullets || [];
28010 // Roo.bootstrap.NavProgressBar.register(this);
28014 * Fires when the active item changes
28015 * @param {Roo.bootstrap.NavProgressBar} this
28016 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28017 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
28024 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
28029 getAutoCreate : function()
28031 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28035 cls : 'roo-navigation-bar-group',
28039 cls : 'roo-navigation-top-bar'
28043 cls : 'roo-navigation-bullets-bar',
28047 cls : 'roo-navigation-bar'
28054 cls : 'roo-navigation-bottom-bar'
28064 initEvents: function()
28069 onRender : function(ct, position)
28071 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28073 if(this.bullets.length){
28074 Roo.each(this.bullets, function(b){
28083 addItem : function(cfg)
28085 var item = new Roo.bootstrap.NavProgressItem(cfg);
28087 item.parentId = this.id;
28088 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28091 var top = new Roo.bootstrap.Element({
28093 cls : 'roo-navigation-bar-text'
28096 var bottom = new Roo.bootstrap.Element({
28098 cls : 'roo-navigation-bar-text'
28101 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28102 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28104 var topText = new Roo.bootstrap.Element({
28106 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28109 var bottomText = new Roo.bootstrap.Element({
28111 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28114 topText.onRender(top.el, null);
28115 bottomText.onRender(bottom.el, null);
28118 item.bottomEl = bottom;
28121 this.barItems.push(item);
28126 getActive : function()
28128 var active = false;
28130 Roo.each(this.barItems, function(v){
28132 if (!v.isActive()) {
28144 setActiveItem : function(item)
28148 Roo.each(this.barItems, function(v){
28149 if (v.rid == item.rid) {
28153 if (v.isActive()) {
28154 v.setActive(false);
28159 item.setActive(true);
28161 this.fireEvent('changed', this, item, prev);
28164 getBarItem: function(rid)
28168 Roo.each(this.barItems, function(e) {
28169 if (e.rid != rid) {
28180 indexOfItem : function(item)
28184 Roo.each(this.barItems, function(v, i){
28186 if (v.rid != item.rid) {
28197 setActiveNext : function()
28199 var i = this.indexOfItem(this.getActive());
28201 if (i > this.barItems.length) {
28205 this.setActiveItem(this.barItems[i+1]);
28208 setActivePrev : function()
28210 var i = this.indexOfItem(this.getActive());
28216 this.setActiveItem(this.barItems[i-1]);
28219 format : function()
28221 if(!this.barItems.length){
28225 var width = 100 / this.barItems.length;
28227 Roo.each(this.barItems, function(i){
28228 i.el.setStyle('width', width + '%');
28229 i.topEl.el.setStyle('width', width + '%');
28230 i.bottomEl.el.setStyle('width', width + '%');
28239 * Nav Progress Item
28244 * @class Roo.bootstrap.NavProgressItem
28245 * @extends Roo.bootstrap.Component
28246 * Bootstrap NavProgressItem class
28247 * @cfg {String} rid the reference id
28248 * @cfg {Boolean} active (true|false) Is item active default false
28249 * @cfg {Boolean} disabled (true|false) Is item active default false
28250 * @cfg {String} html
28251 * @cfg {String} position (top|bottom) text position default bottom
28252 * @cfg {String} icon show icon instead of number
28255 * Create a new NavProgressItem
28256 * @param {Object} config The config object
28258 Roo.bootstrap.NavProgressItem = function(config){
28259 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28264 * The raw click event for the entire grid.
28265 * @param {Roo.bootstrap.NavProgressItem} this
28266 * @param {Roo.EventObject} e
28273 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
28279 position : 'bottom',
28282 getAutoCreate : function()
28284 var iconCls = 'roo-navigation-bar-item-icon';
28286 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28290 cls: 'roo-navigation-bar-item',
28300 cfg.cls += ' active';
28303 cfg.cls += ' disabled';
28309 disable : function()
28311 this.setDisabled(true);
28314 enable : function()
28316 this.setDisabled(false);
28319 initEvents: function()
28321 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28323 this.iconEl.on('click', this.onClick, this);
28326 onClick : function(e)
28328 e.preventDefault();
28334 if(this.fireEvent('click', this, e) === false){
28338 this.parent().setActiveItem(this);
28341 isActive: function ()
28343 return this.active;
28346 setActive : function(state)
28348 if(this.active == state){
28352 this.active = state;
28355 this.el.addClass('active');
28359 this.el.removeClass('active');
28364 setDisabled : function(state)
28366 if(this.disabled == state){
28370 this.disabled = state;
28373 this.el.addClass('disabled');
28377 this.el.removeClass('disabled');
28380 tooltipEl : function()
28382 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28395 * @class Roo.bootstrap.FieldLabel
28396 * @extends Roo.bootstrap.Component
28397 * Bootstrap FieldLabel class
28398 * @cfg {String} html contents of the element
28399 * @cfg {String} tag tag of the element default label
28400 * @cfg {String} cls class of the element
28401 * @cfg {String} target label target
28402 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28403 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28404 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28405 * @cfg {String} iconTooltip default "This field is required"
28408 * Create a new FieldLabel
28409 * @param {Object} config The config object
28412 Roo.bootstrap.FieldLabel = function(config){
28413 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28418 * Fires after the field has been marked as invalid.
28419 * @param {Roo.form.FieldLabel} this
28420 * @param {String} msg The validation message
28425 * Fires after the field has been validated with no errors.
28426 * @param {Roo.form.FieldLabel} this
28432 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28439 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28440 validClass : 'text-success fa fa-lg fa-check',
28441 iconTooltip : 'This field is required',
28443 getAutoCreate : function(){
28447 cls : 'roo-bootstrap-field-label ' + this.cls,
28453 tooltip : this.iconTooltip
28465 initEvents: function()
28467 Roo.bootstrap.Element.superclass.initEvents.call(this);
28469 this.iconEl = this.el.select('i', true).first();
28471 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28473 Roo.bootstrap.FieldLabel.register(this);
28477 * Mark this field as valid
28479 markValid : function()
28481 this.iconEl.show();
28483 this.iconEl.removeClass(this.invalidClass);
28485 this.iconEl.addClass(this.validClass);
28487 this.fireEvent('valid', this);
28491 * Mark this field as invalid
28492 * @param {String} msg The validation message
28494 markInvalid : function(msg)
28496 this.iconEl.show();
28498 this.iconEl.removeClass(this.validClass);
28500 this.iconEl.addClass(this.invalidClass);
28502 this.fireEvent('invalid', this, msg);
28508 Roo.apply(Roo.bootstrap.FieldLabel, {
28513 * register a FieldLabel Group
28514 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28516 register : function(label)
28518 if(this.groups.hasOwnProperty(label.target)){
28522 this.groups[label.target] = label;
28526 * fetch a FieldLabel Group based on the target
28527 * @param {string} target
28528 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28530 get: function(target) {
28531 if (typeof(this.groups[target]) == 'undefined') {
28535 return this.groups[target] ;
28544 * page DateSplitField.
28550 * @class Roo.bootstrap.DateSplitField
28551 * @extends Roo.bootstrap.Component
28552 * Bootstrap DateSplitField class
28553 * @cfg {string} fieldLabel - the label associated
28554 * @cfg {Number} labelWidth set the width of label (0-12)
28555 * @cfg {String} labelAlign (top|left)
28556 * @cfg {Boolean} dayAllowBlank (true|false) default false
28557 * @cfg {Boolean} monthAllowBlank (true|false) default false
28558 * @cfg {Boolean} yearAllowBlank (true|false) default false
28559 * @cfg {string} dayPlaceholder
28560 * @cfg {string} monthPlaceholder
28561 * @cfg {string} yearPlaceholder
28562 * @cfg {string} dayFormat default 'd'
28563 * @cfg {string} monthFormat default 'm'
28564 * @cfg {string} yearFormat default 'Y'
28568 * Create a new DateSplitField
28569 * @param {Object} config The config object
28572 Roo.bootstrap.DateSplitField = function(config){
28573 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28579 * getting the data of years
28580 * @param {Roo.bootstrap.DateSplitField} this
28581 * @param {Object} years
28586 * getting the data of days
28587 * @param {Roo.bootstrap.DateSplitField} this
28588 * @param {Object} days
28593 * Fires after the field has been marked as invalid.
28594 * @param {Roo.form.Field} this
28595 * @param {String} msg The validation message
28600 * Fires after the field has been validated with no errors.
28601 * @param {Roo.form.Field} this
28607 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28610 labelAlign : 'top',
28612 dayAllowBlank : false,
28613 monthAllowBlank : false,
28614 yearAllowBlank : false,
28615 dayPlaceholder : '',
28616 monthPlaceholder : '',
28617 yearPlaceholder : '',
28621 isFormField : true,
28623 getAutoCreate : function()
28627 cls : 'row roo-date-split-field-group',
28632 cls : 'form-hidden-field roo-date-split-field-group-value',
28638 if(this.fieldLabel){
28641 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28645 html : this.fieldLabel
28651 Roo.each(['day', 'month', 'year'], function(t){
28654 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28661 inputEl: function ()
28663 return this.el.select('.roo-date-split-field-group-value', true).first();
28666 onRender : function(ct, position)
28670 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28672 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28674 this.dayField = new Roo.bootstrap.ComboBox({
28675 allowBlank : this.dayAllowBlank,
28676 alwaysQuery : true,
28677 displayField : 'value',
28680 forceSelection : true,
28682 placeholder : this.dayPlaceholder,
28683 selectOnFocus : true,
28684 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28685 triggerAction : 'all',
28687 valueField : 'value',
28688 store : new Roo.data.SimpleStore({
28689 data : (function() {
28691 _this.fireEvent('days', _this, days);
28694 fields : [ 'value' ]
28697 select : function (_self, record, index)
28699 _this.setValue(_this.getValue());
28704 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28706 this.monthField = new Roo.bootstrap.MonthField({
28707 after : '<i class=\"fa fa-calendar\"></i>',
28708 allowBlank : this.monthAllowBlank,
28709 placeholder : this.monthPlaceholder,
28712 render : function (_self)
28714 this.el.select('span.input-group-addon', true).first().on('click', function(e){
28715 e.preventDefault();
28719 select : function (_self, oldvalue, newvalue)
28721 _this.setValue(_this.getValue());
28726 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28728 this.yearField = new Roo.bootstrap.ComboBox({
28729 allowBlank : this.yearAllowBlank,
28730 alwaysQuery : true,
28731 displayField : 'value',
28734 forceSelection : true,
28736 placeholder : this.yearPlaceholder,
28737 selectOnFocus : true,
28738 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28739 triggerAction : 'all',
28741 valueField : 'value',
28742 store : new Roo.data.SimpleStore({
28743 data : (function() {
28745 _this.fireEvent('years', _this, years);
28748 fields : [ 'value' ]
28751 select : function (_self, record, index)
28753 _this.setValue(_this.getValue());
28758 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28761 setValue : function(v, format)
28763 this.inputEl.dom.value = v;
28765 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28767 var d = Date.parseDate(v, f);
28774 this.setDay(d.format(this.dayFormat));
28775 this.setMonth(d.format(this.monthFormat));
28776 this.setYear(d.format(this.yearFormat));
28783 setDay : function(v)
28785 this.dayField.setValue(v);
28786 this.inputEl.dom.value = this.getValue();
28791 setMonth : function(v)
28793 this.monthField.setValue(v, true);
28794 this.inputEl.dom.value = this.getValue();
28799 setYear : function(v)
28801 this.yearField.setValue(v);
28802 this.inputEl.dom.value = this.getValue();
28807 getDay : function()
28809 return this.dayField.getValue();
28812 getMonth : function()
28814 return this.monthField.getValue();
28817 getYear : function()
28819 return this.yearField.getValue();
28822 getValue : function()
28824 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28826 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28836 this.inputEl.dom.value = '';
28841 validate : function()
28843 var d = this.dayField.validate();
28844 var m = this.monthField.validate();
28845 var y = this.yearField.validate();
28850 (!this.dayAllowBlank && !d) ||
28851 (!this.monthAllowBlank && !m) ||
28852 (!this.yearAllowBlank && !y)
28857 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28866 this.markInvalid();
28871 markValid : function()
28874 var label = this.el.select('label', true).first();
28875 var icon = this.el.select('i.fa-star', true).first();
28881 this.fireEvent('valid', this);
28885 * Mark this field as invalid
28886 * @param {String} msg The validation message
28888 markInvalid : function(msg)
28891 var label = this.el.select('label', true).first();
28892 var icon = this.el.select('i.fa-star', true).first();
28894 if(label && !icon){
28895 this.el.select('.roo-date-split-field-label', true).createChild({
28897 cls : 'text-danger fa fa-lg fa-star',
28898 tooltip : 'This field is required',
28899 style : 'margin-right:5px;'
28903 this.fireEvent('invalid', this, msg);
28906 clearInvalid : function()
28908 var label = this.el.select('label', true).first();
28909 var icon = this.el.select('i.fa-star', true).first();
28915 this.fireEvent('valid', this);
28918 getName: function()
28928 * http://masonry.desandro.com
28930 * The idea is to render all the bricks based on vertical width...
28932 * The original code extends 'outlayer' - we might need to use that....
28938 * @class Roo.bootstrap.LayoutMasonry
28939 * @extends Roo.bootstrap.Component
28940 * Bootstrap Layout Masonry class
28943 * Create a new Element
28944 * @param {Object} config The config object
28947 Roo.bootstrap.LayoutMasonry = function(config){
28948 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28954 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
28957 * @cfg {Boolean} isLayoutInstant = no animation?
28959 isLayoutInstant : false, // needed?
28962 * @cfg {Number} boxWidth width of the columns
28967 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
28972 * @cfg {Number} padWidth padding below box..
28977 * @cfg {Number} gutter gutter width..
28982 * @cfg {Number} maxCols maximum number of columns
28988 * @cfg {Boolean} isAutoInitial defalut true
28990 isAutoInitial : true,
28995 * @cfg {Boolean} isHorizontal defalut false
28997 isHorizontal : false,
28999 currentSize : null,
29005 bricks: null, //CompositeElement
29009 _isLayoutInited : false,
29011 // isAlternative : false, // only use for vertical layout...
29014 * @cfg {Number} alternativePadWidth padding below box..
29016 alternativePadWidth : 50,
29018 getAutoCreate : function(){
29022 cls: 'blog-masonary-wrapper ' + this.cls,
29024 cls : 'mas-boxes masonary'
29031 getChildContainer: function( )
29033 if (this.boxesEl) {
29034 return this.boxesEl;
29037 this.boxesEl = this.el.select('.mas-boxes').first();
29039 return this.boxesEl;
29043 initEvents : function()
29047 if(this.isAutoInitial){
29048 Roo.log('hook children rendered');
29049 this.on('childrenrendered', function() {
29050 Roo.log('children rendered');
29056 initial : function()
29058 this.currentSize = this.el.getBox(true);
29060 Roo.EventManager.onWindowResize(this.resize, this);
29062 if(!this.isAutoInitial){
29070 //this.layout.defer(500,this);
29074 resize : function()
29078 var cs = this.el.getBox(true);
29080 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29081 Roo.log("no change in with or X");
29085 this.currentSize = cs;
29091 layout : function()
29093 this._resetLayout();
29095 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29097 this.layoutItems( isInstant );
29099 this._isLayoutInited = true;
29103 _resetLayout : function()
29105 if(this.isHorizontal){
29106 this.horizontalMeasureColumns();
29110 this.verticalMeasureColumns();
29114 verticalMeasureColumns : function()
29116 this.getContainerWidth();
29118 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29119 // this.colWidth = Math.floor(this.containerWidth * 0.8);
29123 var boxWidth = this.boxWidth + this.padWidth;
29125 if(this.containerWidth < this.boxWidth){
29126 boxWidth = this.containerWidth
29129 var containerWidth = this.containerWidth;
29131 var cols = Math.floor(containerWidth / boxWidth);
29133 this.cols = Math.max( cols, 1 );
29135 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29137 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29139 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29141 this.colWidth = boxWidth + avail - this.padWidth;
29143 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29144 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
29147 horizontalMeasureColumns : function()
29149 this.getContainerWidth();
29151 var boxWidth = this.boxWidth;
29153 if(this.containerWidth < boxWidth){
29154 boxWidth = this.containerWidth;
29157 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29159 this.el.setHeight(boxWidth);
29163 getContainerWidth : function()
29165 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
29168 layoutItems : function( isInstant )
29170 var items = Roo.apply([], this.bricks);
29172 if(this.isHorizontal){
29173 this._horizontalLayoutItems( items , isInstant );
29177 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29178 // this._verticalAlternativeLayoutItems( items , isInstant );
29182 this._verticalLayoutItems( items , isInstant );
29186 _verticalLayoutItems : function ( items , isInstant)
29188 if ( !items || !items.length ) {
29193 ['xs', 'xs', 'xs', 'tall'],
29194 ['xs', 'xs', 'tall'],
29195 ['xs', 'xs', 'sm'],
29196 ['xs', 'xs', 'xs'],
29202 ['sm', 'xs', 'xs'],
29206 ['tall', 'xs', 'xs', 'xs'],
29207 ['tall', 'xs', 'xs'],
29219 Roo.each(items, function(item, k){
29221 switch (item.size) {
29222 // these layouts take up a full box,
29233 boxes.push([item]);
29256 var filterPattern = function(box, length)
29264 var pattern = box.slice(0, length);
29268 Roo.each(pattern, function(i){
29269 format.push(i.size);
29272 Roo.each(standard, function(s){
29274 if(String(s) != String(format)){
29283 if(!match && length == 1){
29288 filterPattern(box, length - 1);
29292 queue.push(pattern);
29294 box = box.slice(length, box.length);
29296 filterPattern(box, 4);
29302 Roo.each(boxes, function(box, k){
29308 if(box.length == 1){
29313 filterPattern(box, 4);
29317 this._processVerticalLayoutQueue( queue, isInstant );
29321 // _verticalAlternativeLayoutItems : function( items , isInstant )
29323 // if ( !items || !items.length ) {
29327 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
29331 _horizontalLayoutItems : function ( items , isInstant)
29333 if ( !items || !items.length || items.length < 3) {
29339 var eItems = items.slice(0, 3);
29341 items = items.slice(3, items.length);
29344 ['xs', 'xs', 'xs', 'wide'],
29345 ['xs', 'xs', 'wide'],
29346 ['xs', 'xs', 'sm'],
29347 ['xs', 'xs', 'xs'],
29353 ['sm', 'xs', 'xs'],
29357 ['wide', 'xs', 'xs', 'xs'],
29358 ['wide', 'xs', 'xs'],
29371 Roo.each(items, function(item, k){
29373 switch (item.size) {
29384 boxes.push([item]);
29408 var filterPattern = function(box, length)
29416 var pattern = box.slice(0, length);
29420 Roo.each(pattern, function(i){
29421 format.push(i.size);
29424 Roo.each(standard, function(s){
29426 if(String(s) != String(format)){
29435 if(!match && length == 1){
29440 filterPattern(box, length - 1);
29444 queue.push(pattern);
29446 box = box.slice(length, box.length);
29448 filterPattern(box, 4);
29454 Roo.each(boxes, function(box, k){
29460 if(box.length == 1){
29465 filterPattern(box, 4);
29472 var pos = this.el.getBox(true);
29476 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29478 var hit_end = false;
29480 Roo.each(queue, function(box){
29484 Roo.each(box, function(b){
29486 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29496 Roo.each(box, function(b){
29498 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29501 mx = Math.max(mx, b.x);
29505 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29509 Roo.each(box, function(b){
29511 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29525 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29528 /** Sets position of item in DOM
29529 * @param {Element} item
29530 * @param {Number} x - horizontal position
29531 * @param {Number} y - vertical position
29532 * @param {Boolean} isInstant - disables transitions
29534 _processVerticalLayoutQueue : function( queue, isInstant )
29536 var pos = this.el.getBox(true);
29541 for (var i = 0; i < this.cols; i++){
29545 Roo.each(queue, function(box, k){
29547 var col = k % this.cols;
29549 Roo.each(box, function(b,kk){
29551 b.el.position('absolute');
29553 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29554 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29556 if(b.size == 'md-left' || b.size == 'md-right'){
29557 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29558 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29561 b.el.setWidth(width);
29562 b.el.setHeight(height);
29564 b.el.select('iframe',true).setSize(width,height);
29568 for (var i = 0; i < this.cols; i++){
29570 if(maxY[i] < maxY[col]){
29575 col = Math.min(col, i);
29579 x = pos.x + col * (this.colWidth + this.padWidth);
29583 var positions = [];
29585 switch (box.length){
29587 positions = this.getVerticalOneBoxColPositions(x, y, box);
29590 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29593 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29596 positions = this.getVerticalFourBoxColPositions(x, y, box);
29602 Roo.each(box, function(b,kk){
29604 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29606 var sz = b.el.getSize();
29608 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29616 for (var i = 0; i < this.cols; i++){
29617 mY = Math.max(mY, maxY[i]);
29620 this.el.setHeight(mY - pos.y);
29624 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29626 // var pos = this.el.getBox(true);
29629 // var maxX = pos.right;
29631 // var maxHeight = 0;
29633 // Roo.each(items, function(item, k){
29637 // item.el.position('absolute');
29639 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29641 // item.el.setWidth(width);
29643 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29645 // item.el.setHeight(height);
29648 // item.el.setXY([x, y], isInstant ? false : true);
29650 // item.el.setXY([maxX - width, y], isInstant ? false : true);
29653 // y = y + height + this.alternativePadWidth;
29655 // maxHeight = maxHeight + height + this.alternativePadWidth;
29659 // this.el.setHeight(maxHeight);
29663 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29665 var pos = this.el.getBox(true);
29670 var maxX = pos.right;
29672 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29674 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29676 Roo.each(queue, function(box, k){
29678 Roo.each(box, function(b, kk){
29680 b.el.position('absolute');
29682 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29683 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29685 if(b.size == 'md-left' || b.size == 'md-right'){
29686 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29687 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29690 b.el.setWidth(width);
29691 b.el.setHeight(height);
29699 var positions = [];
29701 switch (box.length){
29703 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29706 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29709 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29712 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29718 Roo.each(box, function(b,kk){
29720 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29722 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29730 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29732 Roo.each(eItems, function(b,k){
29734 b.size = (k == 0) ? 'sm' : 'xs';
29735 b.x = (k == 0) ? 2 : 1;
29736 b.y = (k == 0) ? 2 : 1;
29738 b.el.position('absolute');
29740 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29742 b.el.setWidth(width);
29744 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29746 b.el.setHeight(height);
29750 var positions = [];
29753 x : maxX - this.unitWidth * 2 - this.gutter,
29758 x : maxX - this.unitWidth,
29759 y : minY + (this.unitWidth + this.gutter) * 2
29763 x : maxX - this.unitWidth * 3 - this.gutter * 2,
29767 Roo.each(eItems, function(b,k){
29769 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29775 getVerticalOneBoxColPositions : function(x, y, box)
29779 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29781 if(box[0].size == 'md-left'){
29785 if(box[0].size == 'md-right'){
29790 x : x + (this.unitWidth + this.gutter) * rand,
29797 getVerticalTwoBoxColPositions : function(x, y, box)
29801 if(box[0].size == 'xs'){
29805 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29809 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29823 x : x + (this.unitWidth + this.gutter) * 2,
29824 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29831 getVerticalThreeBoxColPositions : function(x, y, box)
29835 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29843 x : x + (this.unitWidth + this.gutter) * 1,
29848 x : x + (this.unitWidth + this.gutter) * 2,
29856 if(box[0].size == 'xs' && box[1].size == 'xs'){
29865 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29869 x : x + (this.unitWidth + this.gutter) * 1,
29883 x : x + (this.unitWidth + this.gutter) * 2,
29888 x : x + (this.unitWidth + this.gutter) * 2,
29889 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29896 getVerticalFourBoxColPositions : function(x, y, box)
29900 if(box[0].size == 'xs'){
29909 y : y + (this.unitHeight + this.gutter) * 1
29914 y : y + (this.unitHeight + this.gutter) * 2
29918 x : x + (this.unitWidth + this.gutter) * 1,
29932 x : x + (this.unitWidth + this.gutter) * 2,
29937 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29938 y : y + (this.unitHeight + this.gutter) * 1
29942 x : x + (this.unitWidth + this.gutter) * 2,
29943 y : y + (this.unitWidth + this.gutter) * 2
29950 getHorizontalOneBoxColPositions : function(maxX, minY, box)
29954 if(box[0].size == 'md-left'){
29956 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29963 if(box[0].size == 'md-right'){
29965 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29966 y : minY + (this.unitWidth + this.gutter) * 1
29972 var rand = Math.floor(Math.random() * (4 - box[0].y));
29975 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29976 y : minY + (this.unitWidth + this.gutter) * rand
29983 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29987 if(box[0].size == 'xs'){
29990 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29995 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29996 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30004 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30009 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30010 y : minY + (this.unitWidth + this.gutter) * 2
30017 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30021 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30024 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30029 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30030 y : minY + (this.unitWidth + this.gutter) * 1
30034 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30035 y : minY + (this.unitWidth + this.gutter) * 2
30042 if(box[0].size == 'xs' && box[1].size == 'xs'){
30045 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30050 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30055 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30056 y : minY + (this.unitWidth + this.gutter) * 1
30064 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30069 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30070 y : minY + (this.unitWidth + this.gutter) * 2
30074 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30075 y : minY + (this.unitWidth + this.gutter) * 2
30082 getHorizontalFourBoxColPositions : function(maxX, minY, box)
30086 if(box[0].size == 'xs'){
30089 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30094 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30099 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),
30104 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30105 y : minY + (this.unitWidth + this.gutter) * 1
30113 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30118 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30119 y : minY + (this.unitWidth + this.gutter) * 2
30123 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30124 y : minY + (this.unitWidth + this.gutter) * 2
30128 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),
30129 y : minY + (this.unitWidth + this.gutter) * 2
30143 * http://masonry.desandro.com
30145 * The idea is to render all the bricks based on vertical width...
30147 * The original code extends 'outlayer' - we might need to use that....
30153 * @class Roo.bootstrap.LayoutMasonryAuto
30154 * @extends Roo.bootstrap.Component
30155 * Bootstrap Layout Masonry class
30158 * Create a new Element
30159 * @param {Object} config The config object
30162 Roo.bootstrap.LayoutMasonryAuto = function(config){
30163 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30166 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
30169 * @cfg {Boolean} isFitWidth - resize the width..
30171 isFitWidth : false, // options..
30173 * @cfg {Boolean} isOriginLeft = left align?
30175 isOriginLeft : true,
30177 * @cfg {Boolean} isOriginTop = top align?
30179 isOriginTop : false,
30181 * @cfg {Boolean} isLayoutInstant = no animation?
30183 isLayoutInstant : false, // needed?
30185 * @cfg {Boolean} isResizingContainer = not sure if this is used..
30187 isResizingContainer : true,
30189 * @cfg {Number} columnWidth width of the columns
30195 * @cfg {Number} maxCols maximum number of columns
30200 * @cfg {Number} padHeight padding below box..
30206 * @cfg {Boolean} isAutoInitial defalut true
30209 isAutoInitial : true,
30215 initialColumnWidth : 0,
30216 currentSize : null,
30218 colYs : null, // array.
30225 bricks: null, //CompositeElement
30226 cols : 0, // array?
30227 // element : null, // wrapped now this.el
30228 _isLayoutInited : null,
30231 getAutoCreate : function(){
30235 cls: 'blog-masonary-wrapper ' + this.cls,
30237 cls : 'mas-boxes masonary'
30244 getChildContainer: function( )
30246 if (this.boxesEl) {
30247 return this.boxesEl;
30250 this.boxesEl = this.el.select('.mas-boxes').first();
30252 return this.boxesEl;
30256 initEvents : function()
30260 if(this.isAutoInitial){
30261 Roo.log('hook children rendered');
30262 this.on('childrenrendered', function() {
30263 Roo.log('children rendered');
30270 initial : function()
30272 this.reloadItems();
30274 this.currentSize = this.el.getBox(true);
30276 /// was window resize... - let's see if this works..
30277 Roo.EventManager.onWindowResize(this.resize, this);
30279 if(!this.isAutoInitial){
30284 this.layout.defer(500,this);
30287 reloadItems: function()
30289 this.bricks = this.el.select('.masonry-brick', true);
30291 this.bricks.each(function(b) {
30292 //Roo.log(b.getSize());
30293 if (!b.attr('originalwidth')) {
30294 b.attr('originalwidth', b.getSize().width);
30299 Roo.log(this.bricks.elements.length);
30302 resize : function()
30305 var cs = this.el.getBox(true);
30307 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30308 Roo.log("no change in with or X");
30311 this.currentSize = cs;
30315 layout : function()
30318 this._resetLayout();
30319 //this._manageStamps();
30321 // don't animate first layout
30322 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30323 this.layoutItems( isInstant );
30325 // flag for initalized
30326 this._isLayoutInited = true;
30329 layoutItems : function( isInstant )
30331 //var items = this._getItemsForLayout( this.items );
30332 // original code supports filtering layout items.. we just ignore it..
30334 this._layoutItems( this.bricks , isInstant );
30336 this._postLayout();
30338 _layoutItems : function ( items , isInstant)
30340 //this.fireEvent( 'layout', this, items );
30343 if ( !items || !items.elements.length ) {
30344 // no items, emit event with empty array
30349 items.each(function(item) {
30350 Roo.log("layout item");
30352 // get x/y object from method
30353 var position = this._getItemLayoutPosition( item );
30355 position.item = item;
30356 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30357 queue.push( position );
30360 this._processLayoutQueue( queue );
30362 /** Sets position of item in DOM
30363 * @param {Element} item
30364 * @param {Number} x - horizontal position
30365 * @param {Number} y - vertical position
30366 * @param {Boolean} isInstant - disables transitions
30368 _processLayoutQueue : function( queue )
30370 for ( var i=0, len = queue.length; i < len; i++ ) {
30371 var obj = queue[i];
30372 obj.item.position('absolute');
30373 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30379 * Any logic you want to do after each layout,
30380 * i.e. size the container
30382 _postLayout : function()
30384 this.resizeContainer();
30387 resizeContainer : function()
30389 if ( !this.isResizingContainer ) {
30392 var size = this._getContainerSize();
30394 this.el.setSize(size.width,size.height);
30395 this.boxesEl.setSize(size.width,size.height);
30401 _resetLayout : function()
30403 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30404 this.colWidth = this.el.getWidth();
30405 //this.gutter = this.el.getWidth();
30407 this.measureColumns();
30413 this.colYs.push( 0 );
30419 measureColumns : function()
30421 this.getContainerWidth();
30422 // if columnWidth is 0, default to outerWidth of first item
30423 if ( !this.columnWidth ) {
30424 var firstItem = this.bricks.first();
30425 Roo.log(firstItem);
30426 this.columnWidth = this.containerWidth;
30427 if (firstItem && firstItem.attr('originalwidth') ) {
30428 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30430 // columnWidth fall back to item of first element
30431 Roo.log("set column width?");
30432 this.initialColumnWidth = this.columnWidth ;
30434 // if first elem has no width, default to size of container
30439 if (this.initialColumnWidth) {
30440 this.columnWidth = this.initialColumnWidth;
30445 // column width is fixed at the top - however if container width get's smaller we should
30448 // this bit calcs how man columns..
30450 var columnWidth = this.columnWidth += this.gutter;
30452 // calculate columns
30453 var containerWidth = this.containerWidth + this.gutter;
30455 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30456 // fix rounding errors, typically with gutters
30457 var excess = columnWidth - containerWidth % columnWidth;
30460 // if overshoot is less than a pixel, round up, otherwise floor it
30461 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30462 cols = Math[ mathMethod ]( cols );
30463 this.cols = Math.max( cols, 1 );
30464 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30466 // padding positioning..
30467 var totalColWidth = this.cols * this.columnWidth;
30468 var padavail = this.containerWidth - totalColWidth;
30469 // so for 2 columns - we need 3 'pads'
30471 var padNeeded = (1+this.cols) * this.padWidth;
30473 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30475 this.columnWidth += padExtra
30476 //this.padWidth = Math.floor(padavail / ( this.cols));
30478 // adjust colum width so that padding is fixed??
30480 // we have 3 columns ... total = width * 3
30481 // we have X left over... that should be used by
30483 //if (this.expandC) {
30491 getContainerWidth : function()
30493 /* // container is parent if fit width
30494 var container = this.isFitWidth ? this.element.parentNode : this.element;
30495 // check that this.size and size are there
30496 // IE8 triggers resize on body size change, so they might not be
30498 var size = getSize( container ); //FIXME
30499 this.containerWidth = size && size.innerWidth; //FIXME
30502 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30506 _getItemLayoutPosition : function( item ) // what is item?
30508 // we resize the item to our columnWidth..
30510 item.setWidth(this.columnWidth);
30511 item.autoBoxAdjust = false;
30513 var sz = item.getSize();
30515 // how many columns does this brick span
30516 var remainder = this.containerWidth % this.columnWidth;
30518 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30519 // round if off by 1 pixel, otherwise use ceil
30520 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30521 colSpan = Math.min( colSpan, this.cols );
30523 // normally this should be '1' as we dont' currently allow multi width columns..
30525 var colGroup = this._getColGroup( colSpan );
30526 // get the minimum Y value from the columns
30527 var minimumY = Math.min.apply( Math, colGroup );
30528 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30530 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30532 // position the brick
30534 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30535 y: this.currentSize.y + minimumY + this.padHeight
30539 // apply setHeight to necessary columns
30540 var setHeight = minimumY + sz.height + this.padHeight;
30541 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30543 var setSpan = this.cols + 1 - colGroup.length;
30544 for ( var i = 0; i < setSpan; i++ ) {
30545 this.colYs[ shortColIndex + i ] = setHeight ;
30552 * @param {Number} colSpan - number of columns the element spans
30553 * @returns {Array} colGroup
30555 _getColGroup : function( colSpan )
30557 if ( colSpan < 2 ) {
30558 // if brick spans only one column, use all the column Ys
30563 // how many different places could this brick fit horizontally
30564 var groupCount = this.cols + 1 - colSpan;
30565 // for each group potential horizontal position
30566 for ( var i = 0; i < groupCount; i++ ) {
30567 // make an array of colY values for that one group
30568 var groupColYs = this.colYs.slice( i, i + colSpan );
30569 // and get the max value of the array
30570 colGroup[i] = Math.max.apply( Math, groupColYs );
30575 _manageStamp : function( stamp )
30577 var stampSize = stamp.getSize();
30578 var offset = stamp.getBox();
30579 // get the columns that this stamp affects
30580 var firstX = this.isOriginLeft ? offset.x : offset.right;
30581 var lastX = firstX + stampSize.width;
30582 var firstCol = Math.floor( firstX / this.columnWidth );
30583 firstCol = Math.max( 0, firstCol );
30585 var lastCol = Math.floor( lastX / this.columnWidth );
30586 // lastCol should not go over if multiple of columnWidth #425
30587 lastCol -= lastX % this.columnWidth ? 0 : 1;
30588 lastCol = Math.min( this.cols - 1, lastCol );
30590 // set colYs to bottom of the stamp
30591 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30594 for ( var i = firstCol; i <= lastCol; i++ ) {
30595 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30600 _getContainerSize : function()
30602 this.maxY = Math.max.apply( Math, this.colYs );
30607 if ( this.isFitWidth ) {
30608 size.width = this._getContainerFitWidth();
30614 _getContainerFitWidth : function()
30616 var unusedCols = 0;
30617 // count unused columns
30620 if ( this.colYs[i] !== 0 ) {
30625 // fit container to columns that have been used
30626 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30629 needsResizeLayout : function()
30631 var previousWidth = this.containerWidth;
30632 this.getContainerWidth();
30633 return previousWidth !== this.containerWidth;
30648 * @class Roo.bootstrap.MasonryBrick
30649 * @extends Roo.bootstrap.Component
30650 * Bootstrap MasonryBrick class
30653 * Create a new MasonryBrick
30654 * @param {Object} config The config object
30657 Roo.bootstrap.MasonryBrick = function(config){
30658 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30664 * When a MasonryBrick is clcik
30665 * @param {Roo.bootstrap.MasonryBrick} this
30666 * @param {Roo.EventObject} e
30672 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
30675 * @cfg {String} title
30679 * @cfg {String} html
30683 * @cfg {String} bgimage
30687 * @cfg {String} videourl
30691 * @cfg {String} cls
30695 * @cfg {String} href
30699 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30704 * @cfg {String} (center|bottom) placetitle
30708 getAutoCreate : function()
30710 var cls = 'masonry-brick';
30712 if(this.href.length){
30713 cls += ' masonry-brick-link';
30716 if(this.bgimage.length){
30717 cls += ' masonry-brick-image';
30721 cls += ' masonry-' + this.size + '-brick';
30724 if(this.placetitle.length){
30726 switch (this.placetitle) {
30728 cls += ' masonry-center-title';
30731 cls += ' masonry-bottom-title';
30738 if(!this.html.length && !this.bgimage.length){
30739 cls += ' masonry-center-title';
30742 if(!this.html.length && this.bgimage.length){
30743 cls += ' masonry-bottom-title';
30748 cls += ' ' + this.cls;
30752 tag: (this.href.length) ? 'a' : 'div',
30757 cls: 'masonry-brick-paragraph',
30763 if(this.href.length){
30764 cfg.href = this.href;
30767 var cn = cfg.cn[0].cn;
30769 if(this.title.length){
30772 cls: 'masonry-brick-title',
30777 if(this.html.length){
30780 cls: 'masonry-brick-text',
30784 if (!this.title.length && !this.html.length) {
30785 cfg.cn[0].cls += ' hide';
30788 if(this.bgimage.length){
30791 cls: 'masonry-brick-image-view',
30795 if(this.videourl.length){
30796 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30797 // youtube support only?
30800 cls: 'masonry-brick-image-view',
30803 allowfullscreen : true
30812 initEvents: function()
30814 switch (this.size) {
30816 // this.intSize = 1;
30821 // this.intSize = 2;
30828 // this.intSize = 3;
30833 // this.intSize = 3;
30838 // this.intSize = 3;
30843 // this.intSize = 3;
30855 this.el.on('touchstart', this.onTouchStart, this);
30856 this.el.on('touchmove', this.onTouchMove, this);
30857 this.el.on('touchend', this.onTouchEnd, this);
30858 this.el.on('contextmenu', this.onContextMenu, this);
30860 this.el.on('mouseenter' ,this.enter, this);
30861 this.el.on('mouseleave', this.leave, this);
30864 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30865 this.parent().bricks.push(this);
30870 onClick: function(e, el)
30876 var time = this.endTimer - this.startTimer;
30884 e.preventDefault();
30887 enter: function(e, el)
30889 e.preventDefault();
30891 if(this.bgimage.length && this.html.length){
30892 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30896 leave: function(e, el)
30898 e.preventDefault();
30900 if(this.bgimage.length && this.html.length){
30901 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30905 onTouchStart: function(e, el)
30907 // e.preventDefault();
30909 this.touchmoved = false;
30911 if(!this.bgimage.length || !this.html.length){
30915 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30917 this.timer = new Date().getTime();
30921 onTouchMove: function(e, el)
30923 this.touchmoved = true;
30926 onContextMenu : function(e,el)
30928 e.preventDefault();
30929 e.stopPropagation();
30933 onTouchEnd: function(e, el)
30935 // e.preventDefault();
30937 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30944 if(!this.bgimage.length || !this.html.length){
30946 if(this.href.length){
30947 window.location.href = this.href;
30953 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30955 window.location.href = this.href;
30970 * @class Roo.bootstrap.Brick
30971 * @extends Roo.bootstrap.Component
30972 * Bootstrap Brick class
30975 * Create a new Brick
30976 * @param {Object} config The config object
30979 Roo.bootstrap.Brick = function(config){
30980 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30986 * When a Brick is click
30987 * @param {Roo.bootstrap.Brick} this
30988 * @param {Roo.EventObject} e
30994 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
30997 * @cfg {String} title
31001 * @cfg {String} html
31005 * @cfg {String} bgimage
31009 * @cfg {String} cls
31013 * @cfg {String} href
31017 * @cfg {String} video
31021 * @cfg {Boolean} square
31025 getAutoCreate : function()
31027 var cls = 'roo-brick';
31029 if(this.href.length){
31030 cls += ' roo-brick-link';
31033 if(this.bgimage.length){
31034 cls += ' roo-brick-image';
31037 if(!this.html.length && !this.bgimage.length){
31038 cls += ' roo-brick-center-title';
31041 if(!this.html.length && this.bgimage.length){
31042 cls += ' roo-brick-bottom-title';
31046 cls += ' ' + this.cls;
31050 tag: (this.href.length) ? 'a' : 'div',
31055 cls: 'roo-brick-paragraph',
31061 if(this.href.length){
31062 cfg.href = this.href;
31065 var cn = cfg.cn[0].cn;
31067 if(this.title.length){
31070 cls: 'roo-brick-title',
31075 if(this.html.length){
31078 cls: 'roo-brick-text',
31085 if(this.bgimage.length){
31088 cls: 'roo-brick-image-view',
31096 initEvents: function()
31098 if(this.title.length || this.html.length){
31099 this.el.on('mouseenter' ,this.enter, this);
31100 this.el.on('mouseleave', this.leave, this);
31104 Roo.EventManager.onWindowResize(this.resize, this);
31109 resize : function()
31111 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31113 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31114 // paragraph.setHeight(paragraph.getWidth());
31116 if(this.bgimage.length){
31117 var image = this.el.select('.roo-brick-image-view', true).first();
31118 image.setWidth(paragraph.getWidth());
31119 image.setHeight(paragraph.getWidth());
31124 enter: function(e, el)
31126 e.preventDefault();
31128 if(this.bgimage.length){
31129 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31130 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31134 leave: function(e, el)
31136 e.preventDefault();
31138 if(this.bgimage.length){
31139 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31140 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31150 * Ext JS Library 1.1.1
31151 * Copyright(c) 2006-2007, Ext JS, LLC.
31153 * Originally Released Under LGPL - original licence link has changed is not relivant.
31156 * <script type="text/javascript">
31161 * @class Roo.bootstrap.SplitBar
31162 * @extends Roo.util.Observable
31163 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31167 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31168 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31169 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31170 split.minSize = 100;
31171 split.maxSize = 600;
31172 split.animate = true;
31173 split.on('moved', splitterMoved);
31176 * Create a new SplitBar
31177 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
31178 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
31179 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31180 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
31181 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31182 position of the SplitBar).
31184 Roo.bootstrap.SplitBar = function(cfg){
31189 // dragElement : elm
31190 // resizingElement: el,
31192 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31193 // placement : Roo.bootstrap.SplitBar.LEFT ,
31194 // existingProxy ???
31197 this.el = Roo.get(cfg.dragElement, true);
31198 this.el.dom.unselectable = "on";
31200 this.resizingEl = Roo.get(cfg.resizingElement, true);
31204 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31205 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31208 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31211 * The minimum size of the resizing element. (Defaults to 0)
31217 * The maximum size of the resizing element. (Defaults to 2000)
31220 this.maxSize = 2000;
31223 * Whether to animate the transition to the new size
31226 this.animate = false;
31229 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31232 this.useShim = false;
31237 if(!cfg.existingProxy){
31239 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31241 this.proxy = Roo.get(cfg.existingProxy).dom;
31244 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31247 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31250 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31253 this.dragSpecs = {};
31256 * @private The adapter to use to positon and resize elements
31258 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31259 this.adapter.init(this);
31261 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31263 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31264 this.el.addClass("roo-splitbar-h");
31267 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31268 this.el.addClass("roo-splitbar-v");
31274 * Fires when the splitter is moved (alias for {@link #event-moved})
31275 * @param {Roo.bootstrap.SplitBar} this
31276 * @param {Number} newSize the new width or height
31281 * Fires when the splitter is moved
31282 * @param {Roo.bootstrap.SplitBar} this
31283 * @param {Number} newSize the new width or height
31287 * @event beforeresize
31288 * Fires before the splitter is dragged
31289 * @param {Roo.bootstrap.SplitBar} this
31291 "beforeresize" : true,
31293 "beforeapply" : true
31296 Roo.util.Observable.call(this);
31299 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31300 onStartProxyDrag : function(x, y){
31301 this.fireEvent("beforeresize", this);
31303 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
31305 o.enableDisplayMode("block");
31306 // all splitbars share the same overlay
31307 Roo.bootstrap.SplitBar.prototype.overlay = o;
31309 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31310 this.overlay.show();
31311 Roo.get(this.proxy).setDisplayed("block");
31312 var size = this.adapter.getElementSize(this);
31313 this.activeMinSize = this.getMinimumSize();;
31314 this.activeMaxSize = this.getMaximumSize();;
31315 var c1 = size - this.activeMinSize;
31316 var c2 = Math.max(this.activeMaxSize - size, 0);
31317 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31318 this.dd.resetConstraints();
31319 this.dd.setXConstraint(
31320 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
31321 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31323 this.dd.setYConstraint(0, 0);
31325 this.dd.resetConstraints();
31326 this.dd.setXConstraint(0, 0);
31327 this.dd.setYConstraint(
31328 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
31329 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31332 this.dragSpecs.startSize = size;
31333 this.dragSpecs.startPoint = [x, y];
31334 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31338 * @private Called after the drag operation by the DDProxy
31340 onEndProxyDrag : function(e){
31341 Roo.get(this.proxy).setDisplayed(false);
31342 var endPoint = Roo.lib.Event.getXY(e);
31344 this.overlay.hide();
31347 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31348 newSize = this.dragSpecs.startSize +
31349 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31350 endPoint[0] - this.dragSpecs.startPoint[0] :
31351 this.dragSpecs.startPoint[0] - endPoint[0]
31354 newSize = this.dragSpecs.startSize +
31355 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31356 endPoint[1] - this.dragSpecs.startPoint[1] :
31357 this.dragSpecs.startPoint[1] - endPoint[1]
31360 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31361 if(newSize != this.dragSpecs.startSize){
31362 if(this.fireEvent('beforeapply', this, newSize) !== false){
31363 this.adapter.setElementSize(this, newSize);
31364 this.fireEvent("moved", this, newSize);
31365 this.fireEvent("resize", this, newSize);
31371 * Get the adapter this SplitBar uses
31372 * @return The adapter object
31374 getAdapter : function(){
31375 return this.adapter;
31379 * Set the adapter this SplitBar uses
31380 * @param {Object} adapter A SplitBar adapter object
31382 setAdapter : function(adapter){
31383 this.adapter = adapter;
31384 this.adapter.init(this);
31388 * Gets the minimum size for the resizing element
31389 * @return {Number} The minimum size
31391 getMinimumSize : function(){
31392 return this.minSize;
31396 * Sets the minimum size for the resizing element
31397 * @param {Number} minSize The minimum size
31399 setMinimumSize : function(minSize){
31400 this.minSize = minSize;
31404 * Gets the maximum size for the resizing element
31405 * @return {Number} The maximum size
31407 getMaximumSize : function(){
31408 return this.maxSize;
31412 * Sets the maximum size for the resizing element
31413 * @param {Number} maxSize The maximum size
31415 setMaximumSize : function(maxSize){
31416 this.maxSize = maxSize;
31420 * Sets the initialize size for the resizing element
31421 * @param {Number} size The initial size
31423 setCurrentSize : function(size){
31424 var oldAnimate = this.animate;
31425 this.animate = false;
31426 this.adapter.setElementSize(this, size);
31427 this.animate = oldAnimate;
31431 * Destroy this splitbar.
31432 * @param {Boolean} removeEl True to remove the element
31434 destroy : function(removeEl){
31436 this.shim.remove();
31439 this.proxy.parentNode.removeChild(this.proxy);
31447 * @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.
31449 Roo.bootstrap.SplitBar.createProxy = function(dir){
31450 var proxy = new Roo.Element(document.createElement("div"));
31451 proxy.unselectable();
31452 var cls = 'roo-splitbar-proxy';
31453 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31454 document.body.appendChild(proxy.dom);
31459 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31460 * Default Adapter. It assumes the splitter and resizing element are not positioned
31461 * elements and only gets/sets the width of the element. Generally used for table based layouts.
31463 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31466 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31467 // do nothing for now
31468 init : function(s){
31472 * Called before drag operations to get the current size of the resizing element.
31473 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31475 getElementSize : function(s){
31476 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31477 return s.resizingEl.getWidth();
31479 return s.resizingEl.getHeight();
31484 * Called after drag operations to set the size of the resizing element.
31485 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31486 * @param {Number} newSize The new size to set
31487 * @param {Function} onComplete A function to be invoked when resizing is complete
31489 setElementSize : function(s, newSize, onComplete){
31490 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31492 s.resizingEl.setWidth(newSize);
31494 onComplete(s, newSize);
31497 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31502 s.resizingEl.setHeight(newSize);
31504 onComplete(s, newSize);
31507 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31514 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31515 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31516 * Adapter that moves the splitter element to align with the resized sizing element.
31517 * Used with an absolute positioned SplitBar.
31518 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31519 * document.body, make sure you assign an id to the body element.
31521 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31522 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31523 this.container = Roo.get(container);
31526 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31527 init : function(s){
31528 this.basic.init(s);
31531 getElementSize : function(s){
31532 return this.basic.getElementSize(s);
31535 setElementSize : function(s, newSize, onComplete){
31536 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31539 moveSplitter : function(s){
31540 var yes = Roo.bootstrap.SplitBar;
31541 switch(s.placement){
31543 s.el.setX(s.resizingEl.getRight());
31546 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31549 s.el.setY(s.resizingEl.getBottom());
31552 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31559 * Orientation constant - Create a vertical SplitBar
31563 Roo.bootstrap.SplitBar.VERTICAL = 1;
31566 * Orientation constant - Create a horizontal SplitBar
31570 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31573 * Placement constant - The resizing element is to the left of the splitter element
31577 Roo.bootstrap.SplitBar.LEFT = 1;
31580 * Placement constant - The resizing element is to the right of the splitter element
31584 Roo.bootstrap.SplitBar.RIGHT = 2;
31587 * Placement constant - The resizing element is positioned above the splitter element
31591 Roo.bootstrap.SplitBar.TOP = 3;
31594 * Placement constant - The resizing element is positioned under splitter element
31598 Roo.bootstrap.SplitBar.BOTTOM = 4;
31599 Roo.namespace("Roo.bootstrap.layout");/*
31601 * Ext JS Library 1.1.1
31602 * Copyright(c) 2006-2007, Ext JS, LLC.
31604 * Originally Released Under LGPL - original licence link has changed is not relivant.
31607 * <script type="text/javascript">
31611 * @class Roo.bootstrap.layout.Manager
31612 * @extends Roo.bootstrap.Component
31613 * Base class for layout managers.
31615 Roo.bootstrap.layout.Manager = function(config)
31617 Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31623 /** false to disable window resize monitoring @type Boolean */
31624 this.monitorWindowResize = true;
31629 * Fires when a layout is performed.
31630 * @param {Roo.LayoutManager} this
31634 * @event regionresized
31635 * Fires when the user resizes a region.
31636 * @param {Roo.LayoutRegion} region The resized region
31637 * @param {Number} newSize The new size (width for east/west, height for north/south)
31639 "regionresized" : true,
31641 * @event regioncollapsed
31642 * Fires when a region is collapsed.
31643 * @param {Roo.LayoutRegion} region The collapsed region
31645 "regioncollapsed" : true,
31647 * @event regionexpanded
31648 * Fires when a region is expanded.
31649 * @param {Roo.LayoutRegion} region The expanded region
31651 "regionexpanded" : true
31653 this.updating = false;
31656 this.el = Roo.get(config.el);
31662 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31667 monitorWindowResize : true,
31673 onRender : function(ct, position)
31676 this.el = Roo.get(ct);
31682 initEvents: function()
31686 // ie scrollbar fix
31687 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31688 document.body.scroll = "no";
31689 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31690 this.el.position('relative');
31692 this.id = this.el.id;
31693 this.el.addClass("roo-layout-container");
31694 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31695 if(this.el.dom != document.body ) {
31696 this.el.on('resize', this.layout,this);
31697 this.el.on('show', this.layout,this);
31703 * Returns true if this layout is currently being updated
31704 * @return {Boolean}
31706 isUpdating : function(){
31707 return this.updating;
31711 * Suspend the LayoutManager from doing auto-layouts while
31712 * making multiple add or remove calls
31714 beginUpdate : function(){
31715 this.updating = true;
31719 * Restore auto-layouts and optionally disable the manager from performing a layout
31720 * @param {Boolean} noLayout true to disable a layout update
31722 endUpdate : function(noLayout){
31723 this.updating = false;
31729 layout: function(){
31733 onRegionResized : function(region, newSize){
31734 this.fireEvent("regionresized", region, newSize);
31738 onRegionCollapsed : function(region){
31739 this.fireEvent("regioncollapsed", region);
31742 onRegionExpanded : function(region){
31743 this.fireEvent("regionexpanded", region);
31747 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31748 * performs box-model adjustments.
31749 * @return {Object} The size as an object {width: (the width), height: (the height)}
31751 getViewSize : function()
31754 if(this.el.dom != document.body){
31755 size = this.el.getSize();
31757 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31759 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31760 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31765 * Returns the Element this layout is bound to.
31766 * @return {Roo.Element}
31768 getEl : function(){
31773 * Returns the specified region.
31774 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31775 * @return {Roo.LayoutRegion}
31777 getRegion : function(target){
31778 return this.regions[target.toLowerCase()];
31781 onWindowResize : function(){
31782 if(this.monitorWindowResize){
31788 * Ext JS Library 1.1.1
31789 * Copyright(c) 2006-2007, Ext JS, LLC.
31791 * Originally Released Under LGPL - original licence link has changed is not relivant.
31794 * <script type="text/javascript">
31797 * @class Roo.bootstrap.layout.Border
31798 * @extends Roo.bootstrap.layout.Manager
31799 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31800 * please see: examples/bootstrap/nested.html<br><br>
31802 <b>The container the layout is rendered into can be either the body element or any other element.
31803 If it is not the body element, the container needs to either be an absolute positioned element,
31804 or you will need to add "position:relative" to the css of the container. You will also need to specify
31805 the container size if it is not the body element.</b>
31808 * Create a new Border
31809 * @param {Object} config Configuration options
31811 Roo.bootstrap.layout.Border = function(config){
31812 config = config || {};
31813 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31817 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31818 if(config[region]){
31819 config[region].region = region;
31820 this.addRegion(config[region]);
31826 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
31828 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31830 * Creates and adds a new region if it doesn't already exist.
31831 * @param {String} target The target region key (north, south, east, west or center).
31832 * @param {Object} config The regions config object
31833 * @return {BorderLayoutRegion} The new region
31835 addRegion : function(config)
31837 if(!this.regions[config.region]){
31838 var r = this.factory(config);
31839 this.bindRegion(r);
31841 return this.regions[config.region];
31845 bindRegion : function(r){
31846 this.regions[r.config.region] = r;
31848 r.on("visibilitychange", this.layout, this);
31849 r.on("paneladded", this.layout, this);
31850 r.on("panelremoved", this.layout, this);
31851 r.on("invalidated", this.layout, this);
31852 r.on("resized", this.onRegionResized, this);
31853 r.on("collapsed", this.onRegionCollapsed, this);
31854 r.on("expanded", this.onRegionExpanded, this);
31858 * Performs a layout update.
31860 layout : function()
31862 if(this.updating) {
31866 // render all the rebions if they have not been done alreayd?
31867 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31868 if(this.regions[region] && !this.regions[region].bodyEl){
31869 this.regions[region].onRender(this.el)
31873 var size = this.getViewSize();
31874 var w = size.width;
31875 var h = size.height;
31880 //var x = 0, y = 0;
31882 var rs = this.regions;
31883 var north = rs["north"];
31884 var south = rs["south"];
31885 var west = rs["west"];
31886 var east = rs["east"];
31887 var center = rs["center"];
31888 //if(this.hideOnLayout){ // not supported anymore
31889 //c.el.setStyle("display", "none");
31891 if(north && north.isVisible()){
31892 var b = north.getBox();
31893 var m = north.getMargins();
31894 b.width = w - (m.left+m.right);
31897 centerY = b.height + b.y + m.bottom;
31898 centerH -= centerY;
31899 north.updateBox(this.safeBox(b));
31901 if(south && south.isVisible()){
31902 var b = south.getBox();
31903 var m = south.getMargins();
31904 b.width = w - (m.left+m.right);
31906 var totalHeight = (b.height + m.top + m.bottom);
31907 b.y = h - totalHeight + m.top;
31908 centerH -= totalHeight;
31909 south.updateBox(this.safeBox(b));
31911 if(west && west.isVisible()){
31912 var b = west.getBox();
31913 var m = west.getMargins();
31914 b.height = centerH - (m.top+m.bottom);
31916 b.y = centerY + m.top;
31917 var totalWidth = (b.width + m.left + m.right);
31918 centerX += totalWidth;
31919 centerW -= totalWidth;
31920 west.updateBox(this.safeBox(b));
31922 if(east && east.isVisible()){
31923 var b = east.getBox();
31924 var m = east.getMargins();
31925 b.height = centerH - (m.top+m.bottom);
31926 var totalWidth = (b.width + m.left + m.right);
31927 b.x = w - totalWidth + m.left;
31928 b.y = centerY + m.top;
31929 centerW -= totalWidth;
31930 east.updateBox(this.safeBox(b));
31933 var m = center.getMargins();
31935 x: centerX + m.left,
31936 y: centerY + m.top,
31937 width: centerW - (m.left+m.right),
31938 height: centerH - (m.top+m.bottom)
31940 //if(this.hideOnLayout){
31941 //center.el.setStyle("display", "block");
31943 center.updateBox(this.safeBox(centerBox));
31946 this.fireEvent("layout", this);
31950 safeBox : function(box){
31951 box.width = Math.max(0, box.width);
31952 box.height = Math.max(0, box.height);
31957 * Adds a ContentPanel (or subclass) to this layout.
31958 * @param {String} target The target region key (north, south, east, west or center).
31959 * @param {Roo.ContentPanel} panel The panel to add
31960 * @return {Roo.ContentPanel} The added panel
31962 add : function(target, panel){
31964 target = target.toLowerCase();
31965 return this.regions[target].add(panel);
31969 * Remove a ContentPanel (or subclass) to this layout.
31970 * @param {String} target The target region key (north, south, east, west or center).
31971 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31972 * @return {Roo.ContentPanel} The removed panel
31974 remove : function(target, panel){
31975 target = target.toLowerCase();
31976 return this.regions[target].remove(panel);
31980 * Searches all regions for a panel with the specified id
31981 * @param {String} panelId
31982 * @return {Roo.ContentPanel} The panel or null if it wasn't found
31984 findPanel : function(panelId){
31985 var rs = this.regions;
31986 for(var target in rs){
31987 if(typeof rs[target] != "function"){
31988 var p = rs[target].getPanel(panelId);
31998 * Searches all regions for a panel with the specified id and activates (shows) it.
31999 * @param {String/ContentPanel} panelId The panels id or the panel itself
32000 * @return {Roo.ContentPanel} The shown panel or null
32002 showPanel : function(panelId) {
32003 var rs = this.regions;
32004 for(var target in rs){
32005 var r = rs[target];
32006 if(typeof r != "function"){
32007 if(r.hasPanel(panelId)){
32008 return r.showPanel(panelId);
32016 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32017 * @param {Roo.state.Provider} provider (optional) An alternate state provider
32020 restoreState : function(provider){
32022 provider = Roo.state.Manager;
32024 var sm = new Roo.LayoutStateManager();
32025 sm.init(this, provider);
32031 * Adds a xtype elements to the layout.
32035 xtype : 'ContentPanel',
32042 xtype : 'NestedLayoutPanel',
32048 items : [ ... list of content panels or nested layout panels.. ]
32052 * @param {Object} cfg Xtype definition of item to add.
32054 addxtype : function(cfg)
32056 // basically accepts a pannel...
32057 // can accept a layout region..!?!?
32058 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32061 // theory? children can only be panels??
32063 //if (!cfg.xtype.match(/Panel$/)) {
32068 if (typeof(cfg.region) == 'undefined') {
32069 Roo.log("Failed to add Panel, region was not set");
32073 var region = cfg.region;
32079 xitems = cfg.items;
32086 case 'Content': // ContentPanel (el, cfg)
32087 case 'Scroll': // ContentPanel (el, cfg)
32089 cfg.autoCreate = true;
32090 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32092 // var el = this.el.createChild();
32093 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32096 this.add(region, ret);
32100 case 'TreePanel': // our new panel!
32101 cfg.el = this.el.createChild();
32102 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32103 this.add(region, ret);
32108 // create a new Layout (which is a Border Layout...
32110 var clayout = cfg.layout;
32111 clayout.el = this.el.createChild();
32112 clayout.items = clayout.items || [];
32116 // replace this exitems with the clayout ones..
32117 xitems = clayout.items;
32119 // force background off if it's in center...
32120 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32121 cfg.background = false;
32123 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
32126 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32127 //console.log('adding nested layout panel ' + cfg.toSource());
32128 this.add(region, ret);
32129 nb = {}; /// find first...
32134 // needs grid and region
32136 //var el = this.getRegion(region).el.createChild();
32138 *var el = this.el.createChild();
32139 // create the grid first...
32140 cfg.grid.container = el;
32141 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
32144 if (region == 'center' && this.active ) {
32145 cfg.background = false;
32148 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32150 this.add(region, ret);
32152 if (cfg.background) {
32153 // render grid on panel activation (if panel background)
32154 ret.on('activate', function(gp) {
32155 if (!gp.grid.rendered) {
32156 // gp.grid.render(el);
32160 // cfg.grid.render(el);
32166 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
32167 // it was the old xcomponent building that caused this before.
32168 // espeically if border is the top element in the tree.
32178 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32180 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32181 this.add(region, ret);
32185 throw "Can not add '" + cfg.xtype + "' to Border";
32191 this.beginUpdate();
32195 Roo.each(xitems, function(i) {
32196 region = nb && i.region ? i.region : false;
32198 var add = ret.addxtype(i);
32201 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32202 if (!i.background) {
32203 abn[region] = nb[region] ;
32210 // make the last non-background panel active..
32211 //if (nb) { Roo.log(abn); }
32214 for(var r in abn) {
32215 region = this.getRegion(r);
32217 // tried using nb[r], but it does not work..
32219 region.showPanel(abn[r]);
32230 factory : function(cfg)
32233 var validRegions = Roo.bootstrap.layout.Border.regions;
32235 var target = cfg.region;
32238 var r = Roo.bootstrap.layout;
32242 return new r.North(cfg);
32244 return new r.South(cfg);
32246 return new r.East(cfg);
32248 return new r.West(cfg);
32250 return new r.Center(cfg);
32252 throw 'Layout region "'+target+'" not supported.';
32259 * Ext JS Library 1.1.1
32260 * Copyright(c) 2006-2007, Ext JS, LLC.
32262 * Originally Released Under LGPL - original licence link has changed is not relivant.
32265 * <script type="text/javascript">
32269 * @class Roo.bootstrap.layout.Basic
32270 * @extends Roo.util.Observable
32271 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32272 * and does not have a titlebar, tabs or any other features. All it does is size and position
32273 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32274 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32275 * @cfg {string} region the region that it inhabits..
32276 * @cfg {bool} skipConfig skip config?
32280 Roo.bootstrap.layout.Basic = function(config){
32282 this.mgr = config.mgr;
32284 this.position = config.region;
32286 var skipConfig = config.skipConfig;
32290 * @scope Roo.BasicLayoutRegion
32294 * @event beforeremove
32295 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32296 * @param {Roo.LayoutRegion} this
32297 * @param {Roo.ContentPanel} panel The panel
32298 * @param {Object} e The cancel event object
32300 "beforeremove" : true,
32302 * @event invalidated
32303 * Fires when the layout for this region is changed.
32304 * @param {Roo.LayoutRegion} this
32306 "invalidated" : true,
32308 * @event visibilitychange
32309 * Fires when this region is shown or hidden
32310 * @param {Roo.LayoutRegion} this
32311 * @param {Boolean} visibility true or false
32313 "visibilitychange" : true,
32315 * @event paneladded
32316 * Fires when a panel is added.
32317 * @param {Roo.LayoutRegion} this
32318 * @param {Roo.ContentPanel} panel The panel
32320 "paneladded" : true,
32322 * @event panelremoved
32323 * Fires when a panel is removed.
32324 * @param {Roo.LayoutRegion} this
32325 * @param {Roo.ContentPanel} panel The panel
32327 "panelremoved" : true,
32329 * @event beforecollapse
32330 * Fires when this region before collapse.
32331 * @param {Roo.LayoutRegion} this
32333 "beforecollapse" : true,
32336 * Fires when this region is collapsed.
32337 * @param {Roo.LayoutRegion} this
32339 "collapsed" : true,
32342 * Fires when this region is expanded.
32343 * @param {Roo.LayoutRegion} this
32348 * Fires when this region is slid into view.
32349 * @param {Roo.LayoutRegion} this
32351 "slideshow" : true,
32354 * Fires when this region slides out of view.
32355 * @param {Roo.LayoutRegion} this
32357 "slidehide" : true,
32359 * @event panelactivated
32360 * Fires when a panel is activated.
32361 * @param {Roo.LayoutRegion} this
32362 * @param {Roo.ContentPanel} panel The activated panel
32364 "panelactivated" : true,
32367 * Fires when the user resizes this region.
32368 * @param {Roo.LayoutRegion} this
32369 * @param {Number} newSize The new size (width for east/west, height for north/south)
32373 /** A collection of panels in this region. @type Roo.util.MixedCollection */
32374 this.panels = new Roo.util.MixedCollection();
32375 this.panels.getKey = this.getPanelId.createDelegate(this);
32377 this.activePanel = null;
32378 // ensure listeners are added...
32380 if (config.listeners || config.events) {
32381 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32382 listeners : config.listeners || {},
32383 events : config.events || {}
32387 if(skipConfig !== true){
32388 this.applyConfig(config);
32392 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32394 getPanelId : function(p){
32398 applyConfig : function(config){
32399 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32400 this.config = config;
32405 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
32406 * the width, for horizontal (north, south) the height.
32407 * @param {Number} newSize The new width or height
32409 resizeTo : function(newSize){
32410 var el = this.el ? this.el :
32411 (this.activePanel ? this.activePanel.getEl() : null);
32413 switch(this.position){
32416 el.setWidth(newSize);
32417 this.fireEvent("resized", this, newSize);
32421 el.setHeight(newSize);
32422 this.fireEvent("resized", this, newSize);
32428 getBox : function(){
32429 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32432 getMargins : function(){
32433 return this.margins;
32436 updateBox : function(box){
32438 var el = this.activePanel.getEl();
32439 el.dom.style.left = box.x + "px";
32440 el.dom.style.top = box.y + "px";
32441 this.activePanel.setSize(box.width, box.height);
32445 * Returns the container element for this region.
32446 * @return {Roo.Element}
32448 getEl : function(){
32449 return this.activePanel;
32453 * Returns true if this region is currently visible.
32454 * @return {Boolean}
32456 isVisible : function(){
32457 return this.activePanel ? true : false;
32460 setActivePanel : function(panel){
32461 panel = this.getPanel(panel);
32462 if(this.activePanel && this.activePanel != panel){
32463 this.activePanel.setActiveState(false);
32464 this.activePanel.getEl().setLeftTop(-10000,-10000);
32466 this.activePanel = panel;
32467 panel.setActiveState(true);
32469 panel.setSize(this.box.width, this.box.height);
32471 this.fireEvent("panelactivated", this, panel);
32472 this.fireEvent("invalidated");
32476 * Show the specified panel.
32477 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32478 * @return {Roo.ContentPanel} The shown panel or null
32480 showPanel : function(panel){
32481 panel = this.getPanel(panel);
32483 this.setActivePanel(panel);
32489 * Get the active panel for this region.
32490 * @return {Roo.ContentPanel} The active panel or null
32492 getActivePanel : function(){
32493 return this.activePanel;
32497 * Add the passed ContentPanel(s)
32498 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32499 * @return {Roo.ContentPanel} The panel added (if only one was added)
32501 add : function(panel){
32502 if(arguments.length > 1){
32503 for(var i = 0, len = arguments.length; i < len; i++) {
32504 this.add(arguments[i]);
32508 if(this.hasPanel(panel)){
32509 this.showPanel(panel);
32512 var el = panel.getEl();
32513 if(el.dom.parentNode != this.mgr.el.dom){
32514 this.mgr.el.dom.appendChild(el.dom);
32516 if(panel.setRegion){
32517 panel.setRegion(this);
32519 this.panels.add(panel);
32520 el.setStyle("position", "absolute");
32521 if(!panel.background){
32522 this.setActivePanel(panel);
32523 if(this.config.initialSize && this.panels.getCount()==1){
32524 this.resizeTo(this.config.initialSize);
32527 this.fireEvent("paneladded", this, panel);
32532 * Returns true if the panel is in this region.
32533 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32534 * @return {Boolean}
32536 hasPanel : function(panel){
32537 if(typeof panel == "object"){ // must be panel obj
32538 panel = panel.getId();
32540 return this.getPanel(panel) ? true : false;
32544 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32545 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32546 * @param {Boolean} preservePanel Overrides the config preservePanel option
32547 * @return {Roo.ContentPanel} The panel that was removed
32549 remove : function(panel, preservePanel){
32550 panel = this.getPanel(panel);
32555 this.fireEvent("beforeremove", this, panel, e);
32556 if(e.cancel === true){
32559 var panelId = panel.getId();
32560 this.panels.removeKey(panelId);
32565 * Returns the panel specified or null if it's not in this region.
32566 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32567 * @return {Roo.ContentPanel}
32569 getPanel : function(id){
32570 if(typeof id == "object"){ // must be panel obj
32573 return this.panels.get(id);
32577 * Returns this regions position (north/south/east/west/center).
32580 getPosition: function(){
32581 return this.position;
32585 * Ext JS Library 1.1.1
32586 * Copyright(c) 2006-2007, Ext JS, LLC.
32588 * Originally Released Under LGPL - original licence link has changed is not relivant.
32591 * <script type="text/javascript">
32595 * @class Roo.bootstrap.layout.Region
32596 * @extends Roo.bootstrap.layout.Basic
32597 * This class represents a region in a layout manager.
32599 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32600 * @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})
32601 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
32602 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
32603 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
32604 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
32605 * @cfg {String} title The title for the region (overrides panel titles)
32606 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
32607 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32608 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
32609 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32610 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
32611 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32612 * the space available, similar to FireFox 1.5 tabs (defaults to false)
32613 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
32614 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
32615 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
32617 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
32618 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
32619 * @cfg {Boolean} disableTabTips True to disable tab tooltips
32620 * @cfg {Number} width For East/West panels
32621 * @cfg {Number} height For North/South panels
32622 * @cfg {Boolean} split To show the splitter
32623 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
32625 * @cfg {string} cls Extra CSS classes to add to region
32627 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32628 * @cfg {string} region the region that it inhabits..
32631 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
32632 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
32634 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
32635 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
32636 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
32638 Roo.bootstrap.layout.Region = function(config)
32640 this.applyConfig(config);
32642 var mgr = config.mgr;
32643 var pos = config.region;
32644 config.skipConfig = true;
32645 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32648 this.onRender(mgr.el);
32651 this.visible = true;
32652 this.collapsed = false;
32653 this.unrendered_panels = [];
32656 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32658 position: '', // set by wrapper (eg. north/south etc..)
32659 unrendered_panels : null, // unrendered panels.
32660 createBody : function(){
32661 /** This region's body element
32662 * @type Roo.Element */
32663 this.bodyEl = this.el.createChild({
32665 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32669 onRender: function(ctr, pos)
32671 var dh = Roo.DomHelper;
32672 /** This region's container element
32673 * @type Roo.Element */
32674 this.el = dh.append(ctr.dom, {
32676 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32678 /** This region's title element
32679 * @type Roo.Element */
32681 this.titleEl = dh.append(this.el.dom,
32684 unselectable: "on",
32685 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32687 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
32688 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32691 this.titleEl.enableDisplayMode();
32692 /** This region's title text element
32693 * @type HTMLElement */
32694 this.titleTextEl = this.titleEl.dom.firstChild;
32695 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32697 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32698 this.closeBtn.enableDisplayMode();
32699 this.closeBtn.on("click", this.closeClicked, this);
32700 this.closeBtn.hide();
32702 this.createBody(this.config);
32703 if(this.config.hideWhenEmpty){
32705 this.on("paneladded", this.validateVisibility, this);
32706 this.on("panelremoved", this.validateVisibility, this);
32708 if(this.autoScroll){
32709 this.bodyEl.setStyle("overflow", "auto");
32711 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32713 //if(c.titlebar !== false){
32714 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32715 this.titleEl.hide();
32717 this.titleEl.show();
32718 if(this.config.title){
32719 this.titleTextEl.innerHTML = this.config.title;
32723 if(this.config.collapsed){
32724 this.collapse(true);
32726 if(this.config.hidden){
32730 if (this.unrendered_panels && this.unrendered_panels.length) {
32731 for (var i =0;i< this.unrendered_panels.length; i++) {
32732 this.add(this.unrendered_panels[i]);
32734 this.unrendered_panels = null;
32740 applyConfig : function(c)
32743 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32744 var dh = Roo.DomHelper;
32745 if(c.titlebar !== false){
32746 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32747 this.collapseBtn.on("click", this.collapse, this);
32748 this.collapseBtn.enableDisplayMode();
32750 if(c.showPin === true || this.showPin){
32751 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32752 this.stickBtn.enableDisplayMode();
32753 this.stickBtn.on("click", this.expand, this);
32754 this.stickBtn.hide();
32759 /** This region's collapsed element
32760 * @type Roo.Element */
32763 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32764 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32767 if(c.floatable !== false){
32768 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32769 this.collapsedEl.on("click", this.collapseClick, this);
32772 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32773 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32774 id: "message", unselectable: "on", style:{"float":"left"}});
32775 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32777 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32778 this.expandBtn.on("click", this.expand, this);
32782 if(this.collapseBtn){
32783 this.collapseBtn.setVisible(c.collapsible == true);
32786 this.cmargins = c.cmargins || this.cmargins ||
32787 (this.position == "west" || this.position == "east" ?
32788 {top: 0, left: 2, right:2, bottom: 0} :
32789 {top: 2, left: 0, right:0, bottom: 2});
32791 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32794 this.bottomTabs = c.tabPosition != "top";
32796 this.autoScroll = c.autoScroll || false;
32801 this.duration = c.duration || .30;
32802 this.slideDuration = c.slideDuration || .45;
32807 * Returns true if this region is currently visible.
32808 * @return {Boolean}
32810 isVisible : function(){
32811 return this.visible;
32815 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32816 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
32818 //setCollapsedTitle : function(title){
32819 // title = title || " ";
32820 // if(this.collapsedTitleTextEl){
32821 // this.collapsedTitleTextEl.innerHTML = title;
32825 getBox : function(){
32827 // if(!this.collapsed){
32828 b = this.el.getBox(false, true);
32830 // b = this.collapsedEl.getBox(false, true);
32835 getMargins : function(){
32836 return this.margins;
32837 //return this.collapsed ? this.cmargins : this.margins;
32840 highlight : function(){
32841 this.el.addClass("x-layout-panel-dragover");
32844 unhighlight : function(){
32845 this.el.removeClass("x-layout-panel-dragover");
32848 updateBox : function(box)
32850 if (!this.bodyEl) {
32851 return; // not rendered yet..
32855 if(!this.collapsed){
32856 this.el.dom.style.left = box.x + "px";
32857 this.el.dom.style.top = box.y + "px";
32858 this.updateBody(box.width, box.height);
32860 this.collapsedEl.dom.style.left = box.x + "px";
32861 this.collapsedEl.dom.style.top = box.y + "px";
32862 this.collapsedEl.setSize(box.width, box.height);
32865 this.tabs.autoSizeTabs();
32869 updateBody : function(w, h)
32872 this.el.setWidth(w);
32873 w -= this.el.getBorderWidth("rl");
32874 if(this.config.adjustments){
32875 w += this.config.adjustments[0];
32878 if(h !== null && h > 0){
32879 this.el.setHeight(h);
32880 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32881 h -= this.el.getBorderWidth("tb");
32882 if(this.config.adjustments){
32883 h += this.config.adjustments[1];
32885 this.bodyEl.setHeight(h);
32887 h = this.tabs.syncHeight(h);
32890 if(this.panelSize){
32891 w = w !== null ? w : this.panelSize.width;
32892 h = h !== null ? h : this.panelSize.height;
32894 if(this.activePanel){
32895 var el = this.activePanel.getEl();
32896 w = w !== null ? w : el.getWidth();
32897 h = h !== null ? h : el.getHeight();
32898 this.panelSize = {width: w, height: h};
32899 this.activePanel.setSize(w, h);
32901 if(Roo.isIE && this.tabs){
32902 this.tabs.el.repaint();
32907 * Returns the container element for this region.
32908 * @return {Roo.Element}
32910 getEl : function(){
32915 * Hides this region.
32918 //if(!this.collapsed){
32919 this.el.dom.style.left = "-2000px";
32922 // this.collapsedEl.dom.style.left = "-2000px";
32923 // this.collapsedEl.hide();
32925 this.visible = false;
32926 this.fireEvent("visibilitychange", this, false);
32930 * Shows this region if it was previously hidden.
32933 //if(!this.collapsed){
32936 // this.collapsedEl.show();
32938 this.visible = true;
32939 this.fireEvent("visibilitychange", this, true);
32942 closeClicked : function(){
32943 if(this.activePanel){
32944 this.remove(this.activePanel);
32948 collapseClick : function(e){
32950 e.stopPropagation();
32953 e.stopPropagation();
32959 * Collapses this region.
32960 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32963 collapse : function(skipAnim, skipCheck = false){
32964 if(this.collapsed) {
32968 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32970 this.collapsed = true;
32972 this.split.el.hide();
32974 if(this.config.animate && skipAnim !== true){
32975 this.fireEvent("invalidated", this);
32976 this.animateCollapse();
32978 this.el.setLocation(-20000,-20000);
32980 this.collapsedEl.show();
32981 this.fireEvent("collapsed", this);
32982 this.fireEvent("invalidated", this);
32988 animateCollapse : function(){
32993 * Expands this region if it was previously collapsed.
32994 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32995 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32998 expand : function(e, skipAnim){
33000 e.stopPropagation();
33002 if(!this.collapsed || this.el.hasActiveFx()) {
33006 this.afterSlideIn();
33009 this.collapsed = false;
33010 if(this.config.animate && skipAnim !== true){
33011 this.animateExpand();
33015 this.split.el.show();
33017 this.collapsedEl.setLocation(-2000,-2000);
33018 this.collapsedEl.hide();
33019 this.fireEvent("invalidated", this);
33020 this.fireEvent("expanded", this);
33024 animateExpand : function(){
33028 initTabs : function()
33030 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33032 var ts = new Roo.bootstrap.panel.Tabs({
33033 el: this.bodyEl.dom,
33034 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33035 disableTooltips: this.config.disableTabTips,
33036 toolbar : this.config.toolbar
33039 if(this.config.hideTabs){
33040 ts.stripWrap.setDisplayed(false);
33043 ts.resizeTabs = this.config.resizeTabs === true;
33044 ts.minTabWidth = this.config.minTabWidth || 40;
33045 ts.maxTabWidth = this.config.maxTabWidth || 250;
33046 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33047 ts.monitorResize = false;
33048 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33049 ts.bodyEl.addClass('roo-layout-tabs-body');
33050 this.panels.each(this.initPanelAsTab, this);
33053 initPanelAsTab : function(panel){
33054 var ti = this.tabs.addTab(
33058 this.config.closeOnTab && panel.isClosable()
33060 if(panel.tabTip !== undefined){
33061 ti.setTooltip(panel.tabTip);
33063 ti.on("activate", function(){
33064 this.setActivePanel(panel);
33067 if(this.config.closeOnTab){
33068 ti.on("beforeclose", function(t, e){
33070 this.remove(panel);
33076 updatePanelTitle : function(panel, title)
33078 if(this.activePanel == panel){
33079 this.updateTitle(title);
33082 var ti = this.tabs.getTab(panel.getEl().id);
33084 if(panel.tabTip !== undefined){
33085 ti.setTooltip(panel.tabTip);
33090 updateTitle : function(title){
33091 if(this.titleTextEl && !this.config.title){
33092 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
33096 setActivePanel : function(panel)
33098 panel = this.getPanel(panel);
33099 if(this.activePanel && this.activePanel != panel){
33100 this.activePanel.setActiveState(false);
33102 this.activePanel = panel;
33103 panel.setActiveState(true);
33104 if(this.panelSize){
33105 panel.setSize(this.panelSize.width, this.panelSize.height);
33108 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33110 this.updateTitle(panel.getTitle());
33112 this.fireEvent("invalidated", this);
33114 this.fireEvent("panelactivated", this, panel);
33118 * Shows the specified panel.
33119 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33120 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33122 showPanel : function(panel)
33124 panel = this.getPanel(panel);
33127 var tab = this.tabs.getTab(panel.getEl().id);
33128 if(tab.isHidden()){
33129 this.tabs.unhideTab(tab.id);
33133 this.setActivePanel(panel);
33140 * Get the active panel for this region.
33141 * @return {Roo.ContentPanel} The active panel or null
33143 getActivePanel : function(){
33144 return this.activePanel;
33147 validateVisibility : function(){
33148 if(this.panels.getCount() < 1){
33149 this.updateTitle(" ");
33150 this.closeBtn.hide();
33153 if(!this.isVisible()){
33160 * Adds the passed ContentPanel(s) to this region.
33161 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33162 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33164 add : function(panel)
33166 if(arguments.length > 1){
33167 for(var i = 0, len = arguments.length; i < len; i++) {
33168 this.add(arguments[i]);
33173 // if we have not been rendered yet, then we can not really do much of this..
33174 if (!this.bodyEl) {
33175 this.unrendered_panels.push(panel);
33182 if(this.hasPanel(panel)){
33183 this.showPanel(panel);
33186 panel.setRegion(this);
33187 this.panels.add(panel);
33188 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33189 // sinle panel - no tab...?? would it not be better to render it with the tabs,
33190 // and hide them... ???
33191 this.bodyEl.dom.appendChild(panel.getEl().dom);
33192 if(panel.background !== true){
33193 this.setActivePanel(panel);
33195 this.fireEvent("paneladded", this, panel);
33202 this.initPanelAsTab(panel);
33206 if(panel.background !== true){
33207 this.tabs.activate(panel.getEl().id);
33209 this.fireEvent("paneladded", this, panel);
33214 * Hides the tab for the specified panel.
33215 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33217 hidePanel : function(panel){
33218 if(this.tabs && (panel = this.getPanel(panel))){
33219 this.tabs.hideTab(panel.getEl().id);
33224 * Unhides the tab for a previously hidden panel.
33225 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33227 unhidePanel : function(panel){
33228 if(this.tabs && (panel = this.getPanel(panel))){
33229 this.tabs.unhideTab(panel.getEl().id);
33233 clearPanels : function(){
33234 while(this.panels.getCount() > 0){
33235 this.remove(this.panels.first());
33240 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33241 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33242 * @param {Boolean} preservePanel Overrides the config preservePanel option
33243 * @return {Roo.ContentPanel} The panel that was removed
33245 remove : function(panel, preservePanel)
33247 panel = this.getPanel(panel);
33252 this.fireEvent("beforeremove", this, panel, e);
33253 if(e.cancel === true){
33256 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33257 var panelId = panel.getId();
33258 this.panels.removeKey(panelId);
33260 document.body.appendChild(panel.getEl().dom);
33263 this.tabs.removeTab(panel.getEl().id);
33264 }else if (!preservePanel){
33265 this.bodyEl.dom.removeChild(panel.getEl().dom);
33267 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33268 var p = this.panels.first();
33269 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33270 tempEl.appendChild(p.getEl().dom);
33271 this.bodyEl.update("");
33272 this.bodyEl.dom.appendChild(p.getEl().dom);
33274 this.updateTitle(p.getTitle());
33276 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33277 this.setActivePanel(p);
33279 panel.setRegion(null);
33280 if(this.activePanel == panel){
33281 this.activePanel = null;
33283 if(this.config.autoDestroy !== false && preservePanel !== true){
33284 try{panel.destroy();}catch(e){}
33286 this.fireEvent("panelremoved", this, panel);
33291 * Returns the TabPanel component used by this region
33292 * @return {Roo.TabPanel}
33294 getTabs : function(){
33298 createTool : function(parentEl, className){
33299 var btn = Roo.DomHelper.append(parentEl, {
33301 cls: "x-layout-tools-button",
33304 cls: "roo-layout-tools-button-inner " + className,
33308 btn.addClassOnOver("roo-layout-tools-button-over");
33313 * Ext JS Library 1.1.1
33314 * Copyright(c) 2006-2007, Ext JS, LLC.
33316 * Originally Released Under LGPL - original licence link has changed is not relivant.
33319 * <script type="text/javascript">
33325 * @class Roo.SplitLayoutRegion
33326 * @extends Roo.LayoutRegion
33327 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33329 Roo.bootstrap.layout.Split = function(config){
33330 this.cursor = config.cursor;
33331 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33334 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33336 splitTip : "Drag to resize.",
33337 collapsibleSplitTip : "Drag to resize. Double click to hide.",
33338 useSplitTips : false,
33340 applyConfig : function(config){
33341 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33344 onRender : function(ctr,pos) {
33346 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33347 if(!this.config.split){
33352 var splitEl = Roo.DomHelper.append(ctr.dom, {
33354 id: this.el.id + "-split",
33355 cls: "roo-layout-split roo-layout-split-"+this.position,
33358 /** The SplitBar for this region
33359 * @type Roo.SplitBar */
33360 // does not exist yet...
33361 Roo.log([this.position, this.orientation]);
33363 this.split = new Roo.bootstrap.SplitBar({
33364 dragElement : splitEl,
33365 resizingElement: this.el,
33366 orientation : this.orientation
33369 this.split.on("moved", this.onSplitMove, this);
33370 this.split.useShim = this.config.useShim === true;
33371 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33372 if(this.useSplitTips){
33373 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33375 //if(config.collapsible){
33376 // this.split.el.on("dblclick", this.collapse, this);
33379 if(typeof this.config.minSize != "undefined"){
33380 this.split.minSize = this.config.minSize;
33382 if(typeof this.config.maxSize != "undefined"){
33383 this.split.maxSize = this.config.maxSize;
33385 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33386 this.hideSplitter();
33391 getHMaxSize : function(){
33392 var cmax = this.config.maxSize || 10000;
33393 var center = this.mgr.getRegion("center");
33394 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33397 getVMaxSize : function(){
33398 var cmax = this.config.maxSize || 10000;
33399 var center = this.mgr.getRegion("center");
33400 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33403 onSplitMove : function(split, newSize){
33404 this.fireEvent("resized", this, newSize);
33408 * Returns the {@link Roo.SplitBar} for this region.
33409 * @return {Roo.SplitBar}
33411 getSplitBar : function(){
33416 this.hideSplitter();
33417 Roo.bootstrap.layout.Split.superclass.hide.call(this);
33420 hideSplitter : function(){
33422 this.split.el.setLocation(-2000,-2000);
33423 this.split.el.hide();
33429 this.split.el.show();
33431 Roo.bootstrap.layout.Split.superclass.show.call(this);
33434 beforeSlide: function(){
33435 if(Roo.isGecko){// firefox overflow auto bug workaround
33436 this.bodyEl.clip();
33438 this.tabs.bodyEl.clip();
33440 if(this.activePanel){
33441 this.activePanel.getEl().clip();
33443 if(this.activePanel.beforeSlide){
33444 this.activePanel.beforeSlide();
33450 afterSlide : function(){
33451 if(Roo.isGecko){// firefox overflow auto bug workaround
33452 this.bodyEl.unclip();
33454 this.tabs.bodyEl.unclip();
33456 if(this.activePanel){
33457 this.activePanel.getEl().unclip();
33458 if(this.activePanel.afterSlide){
33459 this.activePanel.afterSlide();
33465 initAutoHide : function(){
33466 if(this.autoHide !== false){
33467 if(!this.autoHideHd){
33468 var st = new Roo.util.DelayedTask(this.slideIn, this);
33469 this.autoHideHd = {
33470 "mouseout": function(e){
33471 if(!e.within(this.el, true)){
33475 "mouseover" : function(e){
33481 this.el.on(this.autoHideHd);
33485 clearAutoHide : function(){
33486 if(this.autoHide !== false){
33487 this.el.un("mouseout", this.autoHideHd.mouseout);
33488 this.el.un("mouseover", this.autoHideHd.mouseover);
33492 clearMonitor : function(){
33493 Roo.get(document).un("click", this.slideInIf, this);
33496 // these names are backwards but not changed for compat
33497 slideOut : function(){
33498 if(this.isSlid || this.el.hasActiveFx()){
33501 this.isSlid = true;
33502 if(this.collapseBtn){
33503 this.collapseBtn.hide();
33505 this.closeBtnState = this.closeBtn.getStyle('display');
33506 this.closeBtn.hide();
33508 this.stickBtn.show();
33511 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33512 this.beforeSlide();
33513 this.el.setStyle("z-index", 10001);
33514 this.el.slideIn(this.getSlideAnchor(), {
33515 callback: function(){
33517 this.initAutoHide();
33518 Roo.get(document).on("click", this.slideInIf, this);
33519 this.fireEvent("slideshow", this);
33526 afterSlideIn : function(){
33527 this.clearAutoHide();
33528 this.isSlid = false;
33529 this.clearMonitor();
33530 this.el.setStyle("z-index", "");
33531 if(this.collapseBtn){
33532 this.collapseBtn.show();
33534 this.closeBtn.setStyle('display', this.closeBtnState);
33536 this.stickBtn.hide();
33538 this.fireEvent("slidehide", this);
33541 slideIn : function(cb){
33542 if(!this.isSlid || this.el.hasActiveFx()){
33546 this.isSlid = false;
33547 this.beforeSlide();
33548 this.el.slideOut(this.getSlideAnchor(), {
33549 callback: function(){
33550 this.el.setLeftTop(-10000, -10000);
33552 this.afterSlideIn();
33560 slideInIf : function(e){
33561 if(!e.within(this.el)){
33566 animateCollapse : function(){
33567 this.beforeSlide();
33568 this.el.setStyle("z-index", 20000);
33569 var anchor = this.getSlideAnchor();
33570 this.el.slideOut(anchor, {
33571 callback : function(){
33572 this.el.setStyle("z-index", "");
33573 this.collapsedEl.slideIn(anchor, {duration:.3});
33575 this.el.setLocation(-10000,-10000);
33577 this.fireEvent("collapsed", this);
33584 animateExpand : function(){
33585 this.beforeSlide();
33586 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33587 this.el.setStyle("z-index", 20000);
33588 this.collapsedEl.hide({
33591 this.el.slideIn(this.getSlideAnchor(), {
33592 callback : function(){
33593 this.el.setStyle("z-index", "");
33596 this.split.el.show();
33598 this.fireEvent("invalidated", this);
33599 this.fireEvent("expanded", this);
33627 getAnchor : function(){
33628 return this.anchors[this.position];
33631 getCollapseAnchor : function(){
33632 return this.canchors[this.position];
33635 getSlideAnchor : function(){
33636 return this.sanchors[this.position];
33639 getAlignAdj : function(){
33640 var cm = this.cmargins;
33641 switch(this.position){
33657 getExpandAdj : function(){
33658 var c = this.collapsedEl, cm = this.cmargins;
33659 switch(this.position){
33661 return [-(cm.right+c.getWidth()+cm.left), 0];
33664 return [cm.right+c.getWidth()+cm.left, 0];
33667 return [0, -(cm.top+cm.bottom+c.getHeight())];
33670 return [0, cm.top+cm.bottom+c.getHeight()];
33676 * Ext JS Library 1.1.1
33677 * Copyright(c) 2006-2007, Ext JS, LLC.
33679 * Originally Released Under LGPL - original licence link has changed is not relivant.
33682 * <script type="text/javascript">
33685 * These classes are private internal classes
33687 Roo.bootstrap.layout.Center = function(config){
33688 config.region = "center";
33689 Roo.bootstrap.layout.Region.call(this, config);
33690 this.visible = true;
33691 this.minWidth = config.minWidth || 20;
33692 this.minHeight = config.minHeight || 20;
33695 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33697 // center panel can't be hidden
33701 // center panel can't be hidden
33704 getMinWidth: function(){
33705 return this.minWidth;
33708 getMinHeight: function(){
33709 return this.minHeight;
33722 Roo.bootstrap.layout.North = function(config)
33724 config.region = 'north';
33725 config.cursor = 'n-resize';
33727 Roo.bootstrap.layout.Split.call(this, config);
33731 this.split.placement = Roo.bootstrap.SplitBar.TOP;
33732 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33733 this.split.el.addClass("roo-layout-split-v");
33735 var size = config.initialSize || config.height;
33736 if(typeof size != "undefined"){
33737 this.el.setHeight(size);
33740 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33742 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33746 getBox : function(){
33747 if(this.collapsed){
33748 return this.collapsedEl.getBox();
33750 var box = this.el.getBox();
33752 box.height += this.split.el.getHeight();
33757 updateBox : function(box){
33758 if(this.split && !this.collapsed){
33759 box.height -= this.split.el.getHeight();
33760 this.split.el.setLeft(box.x);
33761 this.split.el.setTop(box.y+box.height);
33762 this.split.el.setWidth(box.width);
33764 if(this.collapsed){
33765 this.updateBody(box.width, null);
33767 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33775 Roo.bootstrap.layout.South = function(config){
33776 config.region = 'south';
33777 config.cursor = 's-resize';
33778 Roo.bootstrap.layout.Split.call(this, config);
33780 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33781 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33782 this.split.el.addClass("roo-layout-split-v");
33784 var size = config.initialSize || config.height;
33785 if(typeof size != "undefined"){
33786 this.el.setHeight(size);
33790 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33791 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33792 getBox : function(){
33793 if(this.collapsed){
33794 return this.collapsedEl.getBox();
33796 var box = this.el.getBox();
33798 var sh = this.split.el.getHeight();
33805 updateBox : function(box){
33806 if(this.split && !this.collapsed){
33807 var sh = this.split.el.getHeight();
33810 this.split.el.setLeft(box.x);
33811 this.split.el.setTop(box.y-sh);
33812 this.split.el.setWidth(box.width);
33814 if(this.collapsed){
33815 this.updateBody(box.width, null);
33817 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33821 Roo.bootstrap.layout.East = function(config){
33822 config.region = "east";
33823 config.cursor = "e-resize";
33824 Roo.bootstrap.layout.Split.call(this, config);
33826 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33827 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33828 this.split.el.addClass("roo-layout-split-h");
33830 var size = config.initialSize || config.width;
33831 if(typeof size != "undefined"){
33832 this.el.setWidth(size);
33835 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33836 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33837 getBox : function(){
33838 if(this.collapsed){
33839 return this.collapsedEl.getBox();
33841 var box = this.el.getBox();
33843 var sw = this.split.el.getWidth();
33850 updateBox : function(box){
33851 if(this.split && !this.collapsed){
33852 var sw = this.split.el.getWidth();
33854 this.split.el.setLeft(box.x);
33855 this.split.el.setTop(box.y);
33856 this.split.el.setHeight(box.height);
33859 if(this.collapsed){
33860 this.updateBody(null, box.height);
33862 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33866 Roo.bootstrap.layout.West = function(config){
33867 config.region = "west";
33868 config.cursor = "w-resize";
33870 Roo.bootstrap.layout.Split.call(this, config);
33872 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33873 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33874 this.split.el.addClass("roo-layout-split-h");
33878 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33879 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33881 onRender: function(ctr, pos)
33883 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33884 var size = this.config.initialSize || this.config.width;
33885 if(typeof size != "undefined"){
33886 this.el.setWidth(size);
33890 getBox : function(){
33891 if(this.collapsed){
33892 return this.collapsedEl.getBox();
33894 var box = this.el.getBox();
33896 box.width += this.split.el.getWidth();
33901 updateBox : function(box){
33902 if(this.split && !this.collapsed){
33903 var sw = this.split.el.getWidth();
33905 this.split.el.setLeft(box.x+box.width);
33906 this.split.el.setTop(box.y);
33907 this.split.el.setHeight(box.height);
33909 if(this.collapsed){
33910 this.updateBody(null, box.height);
33912 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33915 Roo.namespace("Roo.bootstrap.panel");/*
33917 * Ext JS Library 1.1.1
33918 * Copyright(c) 2006-2007, Ext JS, LLC.
33920 * Originally Released Under LGPL - original licence link has changed is not relivant.
33923 * <script type="text/javascript">
33926 * @class Roo.ContentPanel
33927 * @extends Roo.util.Observable
33928 * A basic ContentPanel element.
33929 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
33930 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
33931 * @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
33932 * @cfg {Boolean} closable True if the panel can be closed/removed
33933 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
33934 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33935 * @cfg {Toolbar} toolbar A toolbar for this panel
33936 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
33937 * @cfg {String} title The title for this panel
33938 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33939 * @cfg {String} url Calls {@link #setUrl} with this value
33940 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33941 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
33942 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
33943 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
33946 * Create a new ContentPanel.
33947 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33948 * @param {String/Object} config A string to set only the title or a config object
33949 * @param {String} content (optional) Set the HTML content for this panel
33950 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33952 Roo.bootstrap.panel.Content = function( config){
33954 var el = config.el;
33955 var content = config.content;
33957 if(config.autoCreate){ // xtype is available if this is called from factory
33960 this.el = Roo.get(el);
33961 if(!this.el && config && config.autoCreate){
33962 if(typeof config.autoCreate == "object"){
33963 if(!config.autoCreate.id){
33964 config.autoCreate.id = config.id||el;
33966 this.el = Roo.DomHelper.append(document.body,
33967 config.autoCreate, true);
33969 var elcfg = { tag: "div",
33970 cls: "roo-layout-inactive-content",
33974 elcfg.html = config.html;
33978 this.el = Roo.DomHelper.append(document.body, elcfg , true);
33981 this.closable = false;
33982 this.loaded = false;
33983 this.active = false;
33986 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33988 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33990 this.wrapEl = this.el.wrap();
33992 if (config.toolbar.items) {
33993 ti = config.toolbar.items ;
33994 delete config.toolbar.items ;
33998 this.toolbar.render(this.wrapEl, 'before');
33999 for(var i =0;i < ti.length;i++) {
34000 // Roo.log(['add child', items[i]]);
34001 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34003 this.toolbar.items = nitems;
34004 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34005 delete config.toolbar;
34009 // xtype created footer. - not sure if will work as we normally have to render first..
34010 if (this.footer && !this.footer.el && this.footer.xtype) {
34011 if (!this.wrapEl) {
34012 this.wrapEl = this.el.wrap();
34015 this.footer.container = this.wrapEl.createChild();
34017 this.footer = Roo.factory(this.footer, Roo);
34022 if(typeof config == "string"){
34023 this.title = config;
34025 Roo.apply(this, config);
34029 this.resizeEl = Roo.get(this.resizeEl, true);
34031 this.resizeEl = this.el;
34033 // handle view.xtype
34041 * Fires when this panel is activated.
34042 * @param {Roo.ContentPanel} this
34046 * @event deactivate
34047 * Fires when this panel is activated.
34048 * @param {Roo.ContentPanel} this
34050 "deactivate" : true,
34054 * Fires when this panel is resized if fitToFrame is true.
34055 * @param {Roo.ContentPanel} this
34056 * @param {Number} width The width after any component adjustments
34057 * @param {Number} height The height after any component adjustments
34063 * Fires when this tab is created
34064 * @param {Roo.ContentPanel} this
34075 if(this.autoScroll){
34076 this.resizeEl.setStyle("overflow", "auto");
34078 // fix randome scrolling
34079 //this.el.on('scroll', function() {
34080 // Roo.log('fix random scolling');
34081 // this.scrollTo('top',0);
34084 content = content || this.content;
34086 this.setContent(content);
34088 if(config && config.url){
34089 this.setUrl(this.url, this.params, this.loadOnce);
34094 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34096 if (this.view && typeof(this.view.xtype) != 'undefined') {
34097 this.view.el = this.el.appendChild(document.createElement("div"));
34098 this.view = Roo.factory(this.view);
34099 this.view.render && this.view.render(false, '');
34103 this.fireEvent('render', this);
34106 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34108 setRegion : function(region){
34109 this.region = region;
34111 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
34113 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
34118 * Returns the toolbar for this Panel if one was configured.
34119 * @return {Roo.Toolbar}
34121 getToolbar : function(){
34122 return this.toolbar;
34125 setActiveState : function(active){
34126 this.active = active;
34128 this.fireEvent("deactivate", this);
34130 this.fireEvent("activate", this);
34134 * Updates this panel's element
34135 * @param {String} content The new content
34136 * @param {Boolean} loadScripts (optional) true to look for and process scripts
34138 setContent : function(content, loadScripts){
34139 this.el.update(content, loadScripts);
34142 ignoreResize : function(w, h){
34143 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34146 this.lastSize = {width: w, height: h};
34151 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34152 * @return {Roo.UpdateManager} The UpdateManager
34154 getUpdateManager : function(){
34155 return this.el.getUpdateManager();
34158 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34159 * @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:
34162 url: "your-url.php",
34163 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34164 callback: yourFunction,
34165 scope: yourObject, //(optional scope)
34168 text: "Loading...",
34173 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34174 * 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.
34175 * @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}
34176 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34177 * @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.
34178 * @return {Roo.ContentPanel} this
34181 var um = this.el.getUpdateManager();
34182 um.update.apply(um, arguments);
34188 * 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.
34189 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34190 * @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)
34191 * @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)
34192 * @return {Roo.UpdateManager} The UpdateManager
34194 setUrl : function(url, params, loadOnce){
34195 if(this.refreshDelegate){
34196 this.removeListener("activate", this.refreshDelegate);
34198 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34199 this.on("activate", this.refreshDelegate);
34200 return this.el.getUpdateManager();
34203 _handleRefresh : function(url, params, loadOnce){
34204 if(!loadOnce || !this.loaded){
34205 var updater = this.el.getUpdateManager();
34206 updater.update(url, params, this._setLoaded.createDelegate(this));
34210 _setLoaded : function(){
34211 this.loaded = true;
34215 * Returns this panel's id
34218 getId : function(){
34223 * Returns this panel's element - used by regiosn to add.
34224 * @return {Roo.Element}
34226 getEl : function(){
34227 return this.wrapEl || this.el;
34232 adjustForComponents : function(width, height)
34234 //Roo.log('adjustForComponents ');
34235 if(this.resizeEl != this.el){
34236 width -= this.el.getFrameWidth('lr');
34237 height -= this.el.getFrameWidth('tb');
34240 var te = this.toolbar.getEl();
34241 height -= te.getHeight();
34242 te.setWidth(width);
34245 var te = this.footer.getEl();
34246 Roo.log("footer:" + te.getHeight());
34248 height -= te.getHeight();
34249 te.setWidth(width);
34253 if(this.adjustments){
34254 width += this.adjustments[0];
34255 height += this.adjustments[1];
34257 return {"width": width, "height": height};
34260 setSize : function(width, height){
34261 if(this.fitToFrame && !this.ignoreResize(width, height)){
34262 if(this.fitContainer && this.resizeEl != this.el){
34263 this.el.setSize(width, height);
34265 var size = this.adjustForComponents(width, height);
34266 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34267 this.fireEvent('resize', this, size.width, size.height);
34272 * Returns this panel's title
34275 getTitle : function(){
34280 * Set this panel's title
34281 * @param {String} title
34283 setTitle : function(title){
34284 this.title = title;
34286 this.region.updatePanelTitle(this, title);
34291 * Returns true is this panel was configured to be closable
34292 * @return {Boolean}
34294 isClosable : function(){
34295 return this.closable;
34298 beforeSlide : function(){
34300 this.resizeEl.clip();
34303 afterSlide : function(){
34305 this.resizeEl.unclip();
34309 * Force a content refresh from the URL specified in the {@link #setUrl} method.
34310 * Will fail silently if the {@link #setUrl} method has not been called.
34311 * This does not activate the panel, just updates its content.
34313 refresh : function(){
34314 if(this.refreshDelegate){
34315 this.loaded = false;
34316 this.refreshDelegate();
34321 * Destroys this panel
34323 destroy : function(){
34324 this.el.removeAllListeners();
34325 var tempEl = document.createElement("span");
34326 tempEl.appendChild(this.el.dom);
34327 tempEl.innerHTML = "";
34333 * form - if the content panel contains a form - this is a reference to it.
34334 * @type {Roo.form.Form}
34338 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34339 * This contains a reference to it.
34345 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34355 * @param {Object} cfg Xtype definition of item to add.
34359 getChildContainer: function () {
34360 return this.getEl();
34365 var ret = new Roo.factory(cfg);
34370 if (cfg.xtype.match(/^Form$/)) {
34373 //if (this.footer) {
34374 // el = this.footer.container.insertSibling(false, 'before');
34376 el = this.el.createChild();
34379 this.form = new Roo.form.Form(cfg);
34382 if ( this.form.allItems.length) {
34383 this.form.render(el.dom);
34387 // should only have one of theses..
34388 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34389 // views.. should not be just added - used named prop 'view''
34391 cfg.el = this.el.appendChild(document.createElement("div"));
34394 var ret = new Roo.factory(cfg);
34396 ret.render && ret.render(false, ''); // render blank..
34406 * @class Roo.bootstrap.panel.Grid
34407 * @extends Roo.bootstrap.panel.Content
34409 * Create a new GridPanel.
34410 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34411 * @param {Object} config A the config object
34417 Roo.bootstrap.panel.Grid = function(config)
34421 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34422 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34424 config.el = this.wrapper;
34425 //this.el = this.wrapper;
34427 if (config.container) {
34428 // ctor'ed from a Border/panel.grid
34431 this.wrapper.setStyle("overflow", "hidden");
34432 this.wrapper.addClass('roo-grid-container');
34437 if(config.toolbar){
34438 var tool_el = this.wrapper.createChild();
34439 this.toolbar = Roo.factory(config.toolbar);
34441 if (config.toolbar.items) {
34442 ti = config.toolbar.items ;
34443 delete config.toolbar.items ;
34447 this.toolbar.render(tool_el);
34448 for(var i =0;i < ti.length;i++) {
34449 // Roo.log(['add child', items[i]]);
34450 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34452 this.toolbar.items = nitems;
34454 delete config.toolbar;
34457 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34458 config.grid.scrollBody = true;;
34459 config.grid.monitorWindowResize = false; // turn off autosizing
34460 config.grid.autoHeight = false;
34461 config.grid.autoWidth = false;
34463 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34465 if (config.background) {
34466 // render grid on panel activation (if panel background)
34467 this.on('activate', function(gp) {
34468 if (!gp.grid.rendered) {
34469 gp.grid.render(el);
34470 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34476 this.grid.render(this.wrapper);
34477 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34480 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34481 // ??? needed ??? config.el = this.wrapper;
34486 // xtype created footer. - not sure if will work as we normally have to render first..
34487 if (this.footer && !this.footer.el && this.footer.xtype) {
34489 var ctr = this.grid.getView().getFooterPanel(true);
34490 this.footer.dataSource = this.grid.dataSource;
34491 this.footer = Roo.factory(this.footer, Roo);
34492 this.footer.render(ctr);
34502 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34503 getId : function(){
34504 return this.grid.id;
34508 * Returns the grid for this panel
34509 * @return {Roo.bootstrap.Table}
34511 getGrid : function(){
34515 setSize : function(width, height){
34516 if(!this.ignoreResize(width, height)){
34517 var grid = this.grid;
34518 var size = this.adjustForComponents(width, height);
34519 var gridel = grid.getGridEl();
34520 gridel.setSize(size.width, size.height);
34522 var thd = grid.getGridEl().select('thead',true).first();
34523 var tbd = grid.getGridEl().select('tbody', true).first();
34525 tbd.setSize(width, height - thd.getHeight());
34534 beforeSlide : function(){
34535 this.grid.getView().scroller.clip();
34538 afterSlide : function(){
34539 this.grid.getView().scroller.unclip();
34542 destroy : function(){
34543 this.grid.destroy();
34545 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
34550 * @class Roo.bootstrap.panel.Nest
34551 * @extends Roo.bootstrap.panel.Content
34553 * Create a new Panel, that can contain a layout.Border.
34556 * @param {Roo.BorderLayout} layout The layout for this panel
34557 * @param {String/Object} config A string to set only the title or a config object
34559 Roo.bootstrap.panel.Nest = function(config)
34561 // construct with only one argument..
34562 /* FIXME - implement nicer consturctors
34563 if (layout.layout) {
34565 layout = config.layout;
34566 delete config.layout;
34568 if (layout.xtype && !layout.getEl) {
34569 // then layout needs constructing..
34570 layout = Roo.factory(layout, Roo);
34574 config.el = config.layout.getEl();
34576 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34578 config.layout.monitorWindowResize = false; // turn off autosizing
34579 this.layout = config.layout;
34580 this.layout.getEl().addClass("roo-layout-nested-layout");
34587 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34589 setSize : function(width, height){
34590 if(!this.ignoreResize(width, height)){
34591 var size = this.adjustForComponents(width, height);
34592 var el = this.layout.getEl();
34593 if (size.height < 1) {
34594 el.setWidth(size.width);
34596 el.setSize(size.width, size.height);
34598 var touch = el.dom.offsetWidth;
34599 this.layout.layout();
34600 // ie requires a double layout on the first pass
34601 if(Roo.isIE && !this.initialized){
34602 this.initialized = true;
34603 this.layout.layout();
34608 // activate all subpanels if not currently active..
34610 setActiveState : function(active){
34611 this.active = active;
34613 this.fireEvent("deactivate", this);
34617 this.fireEvent("activate", this);
34618 // not sure if this should happen before or after..
34619 if (!this.layout) {
34620 return; // should not happen..
34623 for (var r in this.layout.regions) {
34624 reg = this.layout.getRegion(r);
34625 if (reg.getActivePanel()) {
34626 //reg.showPanel(reg.getActivePanel()); // force it to activate..
34627 reg.setActivePanel(reg.getActivePanel());
34630 if (!reg.panels.length) {
34633 reg.showPanel(reg.getPanel(0));
34642 * Returns the nested BorderLayout for this panel
34643 * @return {Roo.BorderLayout}
34645 getLayout : function(){
34646 return this.layout;
34650 * Adds a xtype elements to the layout of the nested panel
34654 xtype : 'ContentPanel',
34661 xtype : 'NestedLayoutPanel',
34667 items : [ ... list of content panels or nested layout panels.. ]
34671 * @param {Object} cfg Xtype definition of item to add.
34673 addxtype : function(cfg) {
34674 return this.layout.addxtype(cfg);
34679 * Ext JS Library 1.1.1
34680 * Copyright(c) 2006-2007, Ext JS, LLC.
34682 * Originally Released Under LGPL - original licence link has changed is not relivant.
34685 * <script type="text/javascript">
34688 * @class Roo.TabPanel
34689 * @extends Roo.util.Observable
34690 * A lightweight tab container.
34694 // basic tabs 1, built from existing content
34695 var tabs = new Roo.TabPanel("tabs1");
34696 tabs.addTab("script", "View Script");
34697 tabs.addTab("markup", "View Markup");
34698 tabs.activate("script");
34700 // more advanced tabs, built from javascript
34701 var jtabs = new Roo.TabPanel("jtabs");
34702 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34704 // set up the UpdateManager
34705 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34706 var updater = tab2.getUpdateManager();
34707 updater.setDefaultUrl("ajax1.htm");
34708 tab2.on('activate', updater.refresh, updater, true);
34710 // Use setUrl for Ajax loading
34711 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34712 tab3.setUrl("ajax2.htm", null, true);
34715 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34718 jtabs.activate("jtabs-1");
34721 * Create a new TabPanel.
34722 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34723 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34725 Roo.bootstrap.panel.Tabs = function(config){
34727 * The container element for this TabPanel.
34728 * @type Roo.Element
34730 this.el = Roo.get(config.el);
34733 if(typeof config == "boolean"){
34734 this.tabPosition = config ? "bottom" : "top";
34736 Roo.apply(this, config);
34740 if(this.tabPosition == "bottom"){
34741 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34742 this.el.addClass("roo-tabs-bottom");
34744 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34745 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34746 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34748 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34750 if(this.tabPosition != "bottom"){
34751 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34752 * @type Roo.Element
34754 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34755 this.el.addClass("roo-tabs-top");
34759 this.bodyEl.setStyle("position", "relative");
34761 this.active = null;
34762 this.activateDelegate = this.activate.createDelegate(this);
34767 * Fires when the active tab changes
34768 * @param {Roo.TabPanel} this
34769 * @param {Roo.TabPanelItem} activePanel The new active tab
34773 * @event beforetabchange
34774 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34775 * @param {Roo.TabPanel} this
34776 * @param {Object} e Set cancel to true on this object to cancel the tab change
34777 * @param {Roo.TabPanelItem} tab The tab being changed to
34779 "beforetabchange" : true
34782 Roo.EventManager.onWindowResize(this.onResize, this);
34783 this.cpad = this.el.getPadding("lr");
34784 this.hiddenCount = 0;
34787 // toolbar on the tabbar support...
34788 if (this.toolbar) {
34789 alert("no toolbar support yet");
34790 this.toolbar = false;
34792 var tcfg = this.toolbar;
34793 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
34794 this.toolbar = new Roo.Toolbar(tcfg);
34795 if (Roo.isSafari) {
34796 var tbl = tcfg.container.child('table', true);
34797 tbl.setAttribute('width', '100%');
34805 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34808 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34810 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34812 tabPosition : "top",
34814 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34816 currentTabWidth : 0,
34818 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34822 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34826 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34828 preferredTabWidth : 175,
34830 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34832 resizeTabs : false,
34834 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34836 monitorResize : true,
34838 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
34843 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34844 * @param {String} id The id of the div to use <b>or create</b>
34845 * @param {String} text The text for the tab
34846 * @param {String} content (optional) Content to put in the TabPanelItem body
34847 * @param {Boolean} closable (optional) True to create a close icon on the tab
34848 * @return {Roo.TabPanelItem} The created TabPanelItem
34850 addTab : function(id, text, content, closable)
34852 var item = new Roo.bootstrap.panel.TabItem({
34856 closable : closable
34858 this.addTabItem(item);
34860 item.setContent(content);
34866 * Returns the {@link Roo.TabPanelItem} with the specified id/index
34867 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34868 * @return {Roo.TabPanelItem}
34870 getTab : function(id){
34871 return this.items[id];
34875 * Hides the {@link Roo.TabPanelItem} with the specified id/index
34876 * @param {String/Number} id The id or index of the TabPanelItem to hide.
34878 hideTab : function(id){
34879 var t = this.items[id];
34882 this.hiddenCount++;
34883 this.autoSizeTabs();
34888 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34889 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34891 unhideTab : function(id){
34892 var t = this.items[id];
34894 t.setHidden(false);
34895 this.hiddenCount--;
34896 this.autoSizeTabs();
34901 * Adds an existing {@link Roo.TabPanelItem}.
34902 * @param {Roo.TabPanelItem} item The TabPanelItem to add
34904 addTabItem : function(item){
34905 this.items[item.id] = item;
34906 this.items.push(item);
34907 // if(this.resizeTabs){
34908 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34909 // this.autoSizeTabs();
34911 // item.autoSize();
34916 * Removes a {@link Roo.TabPanelItem}.
34917 * @param {String/Number} id The id or index of the TabPanelItem to remove.
34919 removeTab : function(id){
34920 var items = this.items;
34921 var tab = items[id];
34922 if(!tab) { return; }
34923 var index = items.indexOf(tab);
34924 if(this.active == tab && items.length > 1){
34925 var newTab = this.getNextAvailable(index);
34930 this.stripEl.dom.removeChild(tab.pnode.dom);
34931 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34932 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34934 items.splice(index, 1);
34935 delete this.items[tab.id];
34936 tab.fireEvent("close", tab);
34937 tab.purgeListeners();
34938 this.autoSizeTabs();
34941 getNextAvailable : function(start){
34942 var items = this.items;
34944 // look for a next tab that will slide over to
34945 // replace the one being removed
34946 while(index < items.length){
34947 var item = items[++index];
34948 if(item && !item.isHidden()){
34952 // if one isn't found select the previous tab (on the left)
34955 var item = items[--index];
34956 if(item && !item.isHidden()){
34964 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34965 * @param {String/Number} id The id or index of the TabPanelItem to disable.
34967 disableTab : function(id){
34968 var tab = this.items[id];
34969 if(tab && this.active != tab){
34975 * Enables a {@link Roo.TabPanelItem} that is disabled.
34976 * @param {String/Number} id The id or index of the TabPanelItem to enable.
34978 enableTab : function(id){
34979 var tab = this.items[id];
34984 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34985 * @param {String/Number} id The id or index of the TabPanelItem to activate.
34986 * @return {Roo.TabPanelItem} The TabPanelItem.
34988 activate : function(id){
34989 var tab = this.items[id];
34993 if(tab == this.active || tab.disabled){
34997 this.fireEvent("beforetabchange", this, e, tab);
34998 if(e.cancel !== true && !tab.disabled){
35000 this.active.hide();
35002 this.active = this.items[id];
35003 this.active.show();
35004 this.fireEvent("tabchange", this, this.active);
35010 * Gets the active {@link Roo.TabPanelItem}.
35011 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35013 getActiveTab : function(){
35014 return this.active;
35018 * Updates the tab body element to fit the height of the container element
35019 * for overflow scrolling
35020 * @param {Number} targetHeight (optional) Override the starting height from the elements height
35022 syncHeight : function(targetHeight){
35023 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35024 var bm = this.bodyEl.getMargins();
35025 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35026 this.bodyEl.setHeight(newHeight);
35030 onResize : function(){
35031 if(this.monitorResize){
35032 this.autoSizeTabs();
35037 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35039 beginUpdate : function(){
35040 this.updating = true;
35044 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35046 endUpdate : function(){
35047 this.updating = false;
35048 this.autoSizeTabs();
35052 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35054 autoSizeTabs : function(){
35055 var count = this.items.length;
35056 var vcount = count - this.hiddenCount;
35057 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35060 var w = Math.max(this.el.getWidth() - this.cpad, 10);
35061 var availWidth = Math.floor(w / vcount);
35062 var b = this.stripBody;
35063 if(b.getWidth() > w){
35064 var tabs = this.items;
35065 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35066 if(availWidth < this.minTabWidth){
35067 /*if(!this.sleft){ // incomplete scrolling code
35068 this.createScrollButtons();
35071 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35074 if(this.currentTabWidth < this.preferredTabWidth){
35075 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35081 * Returns the number of tabs in this TabPanel.
35084 getCount : function(){
35085 return this.items.length;
35089 * Resizes all the tabs to the passed width
35090 * @param {Number} The new width
35092 setTabWidth : function(width){
35093 this.currentTabWidth = width;
35094 for(var i = 0, len = this.items.length; i < len; i++) {
35095 if(!this.items[i].isHidden()) {
35096 this.items[i].setWidth(width);
35102 * Destroys this TabPanel
35103 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
35105 destroy : function(removeEl){
35106 Roo.EventManager.removeResizeListener(this.onResize, this);
35107 for(var i = 0, len = this.items.length; i < len; i++){
35108 this.items[i].purgeListeners();
35110 if(removeEl === true){
35111 this.el.update("");
35116 createStrip : function(container)
35118 var strip = document.createElement("nav");
35119 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
35120 container.appendChild(strip);
35124 createStripList : function(strip)
35126 // div wrapper for retard IE
35127 // returns the "tr" element.
35128 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
35129 //'<div class="x-tabs-strip-wrap">'+
35130 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
35131 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
35132 return strip.firstChild; //.firstChild.firstChild.firstChild;
35134 createBody : function(container)
35136 var body = document.createElement("div");
35137 Roo.id(body, "tab-body");
35138 //Roo.fly(body).addClass("x-tabs-body");
35139 Roo.fly(body).addClass("tab-content");
35140 container.appendChild(body);
35143 createItemBody :function(bodyEl, id){
35144 var body = Roo.getDom(id);
35146 body = document.createElement("div");
35149 //Roo.fly(body).addClass("x-tabs-item-body");
35150 Roo.fly(body).addClass("tab-pane");
35151 bodyEl.insertBefore(body, bodyEl.firstChild);
35155 createStripElements : function(stripEl, text, closable)
35157 var td = document.createElement("li"); // was td..
35160 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
35163 stripEl.appendChild(td);
35165 td.className = "x-tabs-closable";
35166 if(!this.closeTpl){
35167 this.closeTpl = new Roo.Template(
35168 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35169 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
35170 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
35173 var el = this.closeTpl.overwrite(td, {"text": text});
35174 var close = el.getElementsByTagName("div")[0];
35175 var inner = el.getElementsByTagName("em")[0];
35176 return {"el": el, "close": close, "inner": inner};
35179 // not sure what this is..
35181 //this.tabTpl = new Roo.Template(
35182 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35183 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
35185 this.tabTpl = new Roo.Template(
35187 '<span unselectable="on"' +
35188 (this.disableTooltips ? '' : ' title="{text}"') +
35189 ' >{text}</span></span></a>'
35193 var el = this.tabTpl.overwrite(td, {"text": text});
35194 var inner = el.getElementsByTagName("span")[0];
35195 return {"el": el, "inner": inner};
35203 * @class Roo.TabPanelItem
35204 * @extends Roo.util.Observable
35205 * Represents an individual item (tab plus body) in a TabPanel.
35206 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35207 * @param {String} id The id of this TabPanelItem
35208 * @param {String} text The text for the tab of this TabPanelItem
35209 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35211 Roo.bootstrap.panel.TabItem = function(config){
35213 * The {@link Roo.TabPanel} this TabPanelItem belongs to
35214 * @type Roo.TabPanel
35216 this.tabPanel = config.panel;
35218 * The id for this TabPanelItem
35221 this.id = config.id;
35223 this.disabled = false;
35225 this.text = config.text;
35227 this.loaded = false;
35228 this.closable = config.closable;
35231 * The body element for this TabPanelItem.
35232 * @type Roo.Element
35234 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35235 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35236 this.bodyEl.setStyle("display", "block");
35237 this.bodyEl.setStyle("zoom", "1");
35238 //this.hideAction();
35240 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
35242 this.el = Roo.get(els.el);
35243 this.inner = Roo.get(els.inner, true);
35244 this.textEl = Roo.get(this.el.dom.firstChild, true);
35245 this.pnode = Roo.get(els.el.parentNode, true);
35246 this.el.on("mousedown", this.onTabMouseDown, this);
35247 this.el.on("click", this.onTabClick, this);
35249 if(config.closable){
35250 var c = Roo.get(els.close, true);
35251 c.dom.title = this.closeText;
35252 c.addClassOnOver("close-over");
35253 c.on("click", this.closeClick, this);
35259 * Fires when this tab becomes the active tab.
35260 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35261 * @param {Roo.TabPanelItem} this
35265 * @event beforeclose
35266 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35267 * @param {Roo.TabPanelItem} this
35268 * @param {Object} e Set cancel to true on this object to cancel the close.
35270 "beforeclose": true,
35273 * Fires when this tab is closed.
35274 * @param {Roo.TabPanelItem} this
35278 * @event deactivate
35279 * Fires when this tab is no longer the active tab.
35280 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35281 * @param {Roo.TabPanelItem} this
35283 "deactivate" : true
35285 this.hidden = false;
35287 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35290 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35292 purgeListeners : function(){
35293 Roo.util.Observable.prototype.purgeListeners.call(this);
35294 this.el.removeAllListeners();
35297 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35300 this.pnode.addClass("active");
35303 this.tabPanel.stripWrap.repaint();
35305 this.fireEvent("activate", this.tabPanel, this);
35309 * Returns true if this tab is the active tab.
35310 * @return {Boolean}
35312 isActive : function(){
35313 return this.tabPanel.getActiveTab() == this;
35317 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35320 this.pnode.removeClass("active");
35322 this.fireEvent("deactivate", this.tabPanel, this);
35325 hideAction : function(){
35326 this.bodyEl.hide();
35327 this.bodyEl.setStyle("position", "absolute");
35328 this.bodyEl.setLeft("-20000px");
35329 this.bodyEl.setTop("-20000px");
35332 showAction : function(){
35333 this.bodyEl.setStyle("position", "relative");
35334 this.bodyEl.setTop("");
35335 this.bodyEl.setLeft("");
35336 this.bodyEl.show();
35340 * Set the tooltip for the tab.
35341 * @param {String} tooltip The tab's tooltip
35343 setTooltip : function(text){
35344 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35345 this.textEl.dom.qtip = text;
35346 this.textEl.dom.removeAttribute('title');
35348 this.textEl.dom.title = text;
35352 onTabClick : function(e){
35353 e.preventDefault();
35354 this.tabPanel.activate(this.id);
35357 onTabMouseDown : function(e){
35358 e.preventDefault();
35359 this.tabPanel.activate(this.id);
35362 getWidth : function(){
35363 return this.inner.getWidth();
35366 setWidth : function(width){
35367 var iwidth = width - this.pnode.getPadding("lr");
35368 this.inner.setWidth(iwidth);
35369 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35370 this.pnode.setWidth(width);
35374 * Show or hide the tab
35375 * @param {Boolean} hidden True to hide or false to show.
35377 setHidden : function(hidden){
35378 this.hidden = hidden;
35379 this.pnode.setStyle("display", hidden ? "none" : "");
35383 * Returns true if this tab is "hidden"
35384 * @return {Boolean}
35386 isHidden : function(){
35387 return this.hidden;
35391 * Returns the text for this tab
35394 getText : function(){
35398 autoSize : function(){
35399 //this.el.beginMeasure();
35400 this.textEl.setWidth(1);
35402 * #2804 [new] Tabs in Roojs
35403 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35405 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35406 //this.el.endMeasure();
35410 * Sets the text for the tab (Note: this also sets the tooltip text)
35411 * @param {String} text The tab's text and tooltip
35413 setText : function(text){
35415 this.textEl.update(text);
35416 this.setTooltip(text);
35417 //if(!this.tabPanel.resizeTabs){
35418 // this.autoSize();
35422 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35424 activate : function(){
35425 this.tabPanel.activate(this.id);
35429 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35431 disable : function(){
35432 if(this.tabPanel.active != this){
35433 this.disabled = true;
35434 this.pnode.addClass("disabled");
35439 * Enables this TabPanelItem if it was previously disabled.
35441 enable : function(){
35442 this.disabled = false;
35443 this.pnode.removeClass("disabled");
35447 * Sets the content for this TabPanelItem.
35448 * @param {String} content The content
35449 * @param {Boolean} loadScripts true to look for and load scripts
35451 setContent : function(content, loadScripts){
35452 this.bodyEl.update(content, loadScripts);
35456 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35457 * @return {Roo.UpdateManager} The UpdateManager
35459 getUpdateManager : function(){
35460 return this.bodyEl.getUpdateManager();
35464 * Set a URL to be used to load the content for this TabPanelItem.
35465 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35466 * @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)
35467 * @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)
35468 * @return {Roo.UpdateManager} The UpdateManager
35470 setUrl : function(url, params, loadOnce){
35471 if(this.refreshDelegate){
35472 this.un('activate', this.refreshDelegate);
35474 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35475 this.on("activate", this.refreshDelegate);
35476 return this.bodyEl.getUpdateManager();
35480 _handleRefresh : function(url, params, loadOnce){
35481 if(!loadOnce || !this.loaded){
35482 var updater = this.bodyEl.getUpdateManager();
35483 updater.update(url, params, this._setLoaded.createDelegate(this));
35488 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
35489 * Will fail silently if the setUrl method has not been called.
35490 * This does not activate the panel, just updates its content.
35492 refresh : function(){
35493 if(this.refreshDelegate){
35494 this.loaded = false;
35495 this.refreshDelegate();
35500 _setLoaded : function(){
35501 this.loaded = true;
35505 closeClick : function(e){
35508 this.fireEvent("beforeclose", this, o);
35509 if(o.cancel !== true){
35510 this.tabPanel.removeTab(this.id);
35514 * The text displayed in the tooltip for the close icon.
35517 closeText : "Close this tab"