4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
105 cfg.id = this.id || Roo.id();
107 // fill in the extra attributes
108 if (this.xattr && typeof(this.xattr) =='object') {
109 for (var i in this.xattr) {
110 cfg[i] = this.xattr[i];
115 cfg.dataId = this.dataId;
119 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
122 if (this.style) { // fixme needs to support more complex style data.
123 cfg.style = this.style;
127 cfg.name = this.name;
130 this.el = ct.createChild(cfg, position);
133 this.tooltipEl().attr('tooltip', this.tooltip);
136 if(this.tabIndex !== undefined){
137 this.el.dom.setAttribute('tabIndex', this.tabIndex);
144 * Fetch the element to add children to
145 * @return {Roo.Element} defaults to this.el
147 getChildContainer : function()
152 * Fetch the element to display the tooltip on.
153 * @return {Roo.Element} defaults to this.el
155 tooltipEl : function()
160 addxtype : function(tree,cntr)
164 cn = Roo.factory(tree);
165 //Roo.log(['addxtype', cn]);
167 cn.parentType = this.xtype; //??
168 cn.parentId = this.id;
170 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171 if (typeof(cn.container_method) == 'string') {
172 cntr = cn.container_method;
176 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
178 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
180 var build_from_html = Roo.XComponent.build_from_html;
182 var is_body = (tree.xtype == 'Body') ;
184 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186 var self_cntr_el = Roo.get(this[cntr](false));
188 // do not try and build conditional elements
189 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
193 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195 return this.addxtypeChild(tree,cntr, is_body);
198 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
201 return this.addxtypeChild(Roo.apply({}, tree),cntr);
204 Roo.log('skipping render');
210 if (!build_from_html) {
214 // this i think handles overlaying multiple children of the same type
215 // with the sam eelement.. - which might be buggy..
217 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
223 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
227 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
234 addxtypeChild : function (tree, cntr, is_body)
236 Roo.debug && Roo.log('addxtypeChild:' + cntr);
238 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
241 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242 (typeof(tree['flexy:foreach']) != 'undefined');
246 skip_children = false;
247 // render the element if it's not BODY.
250 cn = Roo.factory(tree);
252 cn.parentType = this.xtype; //??
253 cn.parentId = this.id;
255 var build_from_html = Roo.XComponent.build_from_html;
258 // does the container contain child eleemnts with 'xtype' attributes.
259 // that match this xtype..
260 // note - when we render we create these as well..
261 // so we should check to see if body has xtype set.
262 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
264 var self_cntr_el = Roo.get(this[cntr](false));
265 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
267 //Roo.log(Roo.XComponent.build_from_html);
268 //Roo.log("got echild:");
271 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272 // and are not displayed -this causes this to use up the wrong element when matching.
273 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
276 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
283 //echild.dom.removeAttribute('xtype');
285 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286 Roo.debug && Roo.log(self_cntr_el);
287 Roo.debug && Roo.log(echild);
288 Roo.debug && Roo.log(cn);
294 // if object has flexy:if - then it may or may not be rendered.
295 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
296 // skip a flexy if element.
297 Roo.debug && Roo.log('skipping render');
298 Roo.debug && Roo.log(tree);
300 Roo.debug && Roo.log('skipping all children');
301 skip_children = true;
306 // actually if flexy:foreach is found, we really want to create
307 // multiple copies here...
309 //Roo.log(this[cntr]());
310 // some elements do not have render methods.. like the layouts...
311 cn.render && cn.render(this[cntr](true));
313 // then add the element..
321 if (typeof (tree.menu) != 'undefined') {
322 tree.menu.parentType = cn.xtype;
323 tree.menu.triggerEl = cn.el;
324 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
328 if (!tree.items || !tree.items.length) {
330 //Roo.log(["no children", this]);
335 var items = tree.items;
338 //Roo.log(items.length);
340 if (!skip_children) {
341 for(var i =0;i < items.length;i++) {
342 // Roo.log(['add child', items[i]]);
343 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
349 //Roo.log("fire childrenrendered");
351 cn.fireEvent('childrenrendered', this);
356 * Show a component - removes 'hidden' class
361 this.el.removeClass('hidden');
365 * Hide a component - adds 'hidden' class
369 if (this.el && !this.el.hasClass('hidden')) {
370 this.el.addClass('hidden');
384 * @class Roo.bootstrap.Body
385 * @extends Roo.bootstrap.Component
386 * Bootstrap Body class
390 * @param {Object} config The config object
393 Roo.bootstrap.Body = function(config){
394 Roo.bootstrap.Body.superclass.constructor.call(this, config);
395 this.el = Roo.get(document.body);
396 if (this.cls && this.cls.length) {
397 Roo.get(document.body).addClass(this.cls);
401 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
403 is_body : true,// just to make sure it's constructed?
408 onRender : function(ct, position)
410 /* Roo.log("Roo.bootstrap.Body - onRender");
411 if (this.cls && this.cls.length) {
412 Roo.get(document.body).addClass(this.cls);
432 * @class Roo.bootstrap.ButtonGroup
433 * @extends Roo.bootstrap.Component
434 * Bootstrap ButtonGroup class
435 * @cfg {String} size lg | sm | xs (default empty normal)
436 * @cfg {String} align vertical | justified (default none)
437 * @cfg {String} direction up | down (default down)
438 * @cfg {Boolean} toolbar false | true
439 * @cfg {Boolean} btn true | false
444 * @param {Object} config The config object
447 Roo.bootstrap.ButtonGroup = function(config){
448 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
459 getAutoCreate : function(){
465 cfg.html = this.html || cfg.html;
476 if (['vertical','justified'].indexOf(this.align)!==-1) {
477 cfg.cls = 'btn-group-' + this.align;
479 if (this.align == 'justified') {
480 console.log(this.items);
484 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
485 cfg.cls += ' btn-group-' + this.size;
488 if (this.direction == 'up') {
489 cfg.cls += ' dropup' ;
505 * @class Roo.bootstrap.Button
506 * @extends Roo.bootstrap.Component
507 * Bootstrap Button class
508 * @cfg {String} html The button content
509 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
510 * @cfg {String} size ( lg | sm | xs)
511 * @cfg {String} tag ( a | input | submit)
512 * @cfg {String} href empty or href
513 * @cfg {Boolean} disabled default false;
514 * @cfg {Boolean} isClose default false;
515 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
516 * @cfg {String} badge text for badge
517 * @cfg {String} theme default
518 * @cfg {Boolean} inverse
519 * @cfg {Boolean} toggle
520 * @cfg {String} ontext text for on toggle state
521 * @cfg {String} offtext text for off toggle state
522 * @cfg {Boolean} defaulton
523 * @cfg {Boolean} preventDefault default true
524 * @cfg {Boolean} removeClass remove the standard class..
525 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
528 * Create a new button
529 * @param {Object} config The config object
533 Roo.bootstrap.Button = function(config){
534 Roo.bootstrap.Button.superclass.constructor.call(this, config);
539 * When a butotn is pressed
540 * @param {Roo.bootstrap.Button} this
541 * @param {Roo.EventObject} e
546 * After the button has been toggles
547 * @param {Roo.EventObject} e
548 * @param {boolean} pressed (also available as button.pressed)
554 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
572 preventDefault: true,
581 getAutoCreate : function(){
589 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
590 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
595 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
597 if (this.toggle == true) {
600 cls: 'slider-frame roo-button',
605 'data-off-text':'OFF',
606 cls: 'slider-button',
612 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
613 cfg.cls += ' '+this.weight;
622 cfg["aria-hidden"] = true;
624 cfg.html = "×";
630 if (this.theme==='default') {
631 cfg.cls = 'btn roo-button';
633 //if (this.parentType != 'Navbar') {
634 this.weight = this.weight.length ? this.weight : 'default';
636 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
638 cfg.cls += ' btn-' + this.weight;
640 } else if (this.theme==='glow') {
643 cfg.cls = 'btn-glow roo-button';
645 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
647 cfg.cls += ' ' + this.weight;
653 this.cls += ' inverse';
658 cfg.cls += ' active';
662 cfg.disabled = 'disabled';
666 Roo.log('changing to ul' );
668 this.glyphicon = 'caret';
671 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
673 //gsRoo.log(this.parentType);
674 if (this.parentType === 'Navbar' && !this.parent().bar) {
675 Roo.log('changing to li?');
684 href : this.href || '#'
687 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
688 cfg.cls += ' dropdown';
695 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
697 if (this.glyphicon) {
698 cfg.html = ' ' + cfg.html;
703 cls: 'glyphicon glyphicon-' + this.glyphicon
713 // cfg.cls='btn roo-button';
717 var value = cfg.html;
722 cls: 'glyphicon glyphicon-' + this.glyphicon,
741 cfg.cls += ' dropdown';
742 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
745 if (cfg.tag !== 'a' && this.href !== '') {
746 throw "Tag must be a to set href.";
747 } else if (this.href.length > 0) {
748 cfg.href = this.href;
751 if(this.removeClass){
756 cfg.target = this.target;
761 initEvents: function() {
762 // Roo.log('init events?');
763 // Roo.log(this.el.dom);
766 if (typeof (this.menu) != 'undefined') {
767 this.menu.parentType = this.xtype;
768 this.menu.triggerEl = this.el;
769 this.addxtype(Roo.apply({}, this.menu));
773 if (this.el.hasClass('roo-button')) {
774 this.el.on('click', this.onClick, this);
776 this.el.select('.roo-button').on('click', this.onClick, this);
779 if(this.removeClass){
780 this.el.on('click', this.onClick, this);
783 this.el.enableDisplayMode();
786 onClick : function(e)
793 Roo.log('button on click ');
794 if(this.preventDefault){
797 if (this.pressed === true || this.pressed === false) {
798 this.pressed = !this.pressed;
799 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
800 this.fireEvent('toggle', this, e, this.pressed);
804 this.fireEvent('click', this, e);
808 * Enables this button
812 this.disabled = false;
813 this.el.removeClass('disabled');
817 * Disable this button
821 this.disabled = true;
822 this.el.addClass('disabled');
825 * sets the active state on/off,
826 * @param {Boolean} state (optional) Force a particular state
828 setActive : function(v) {
830 this.el[v ? 'addClass' : 'removeClass']('active');
833 * toggles the current active state
835 toggleActive : function()
837 var active = this.el.hasClass('active');
838 this.setActive(!active);
842 setText : function(str)
844 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
848 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
871 * @class Roo.bootstrap.Column
872 * @extends Roo.bootstrap.Component
873 * Bootstrap Column class
874 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
875 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
876 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
877 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
878 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
879 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
880 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
881 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
884 * @cfg {Boolean} hidden (true|false) hide the element
885 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
886 * @cfg {String} fa (ban|check|...) font awesome icon
887 * @cfg {Number} fasize (1|2|....) font awsome size
889 * @cfg {String} icon (info-sign|check|...) glyphicon name
891 * @cfg {String} html content of column.
894 * Create a new Column
895 * @param {Object} config The config object
898 Roo.bootstrap.Column = function(config){
899 Roo.bootstrap.Column.superclass.constructor.call(this, config);
902 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
920 getAutoCreate : function(){
921 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
929 ['xs','sm','md','lg'].map(function(size){
930 //Roo.log( size + ':' + settings[size]);
932 if (settings[size+'off'] !== false) {
933 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
936 if (settings[size] === false) {
940 if (!settings[size]) { // 0 = hidden
941 cfg.cls += ' hidden-' + size;
944 cfg.cls += ' col-' + size + '-' + settings[size];
949 cfg.cls += ' hidden';
952 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
953 cfg.cls +=' alert alert-' + this.alert;
957 if (this.html.length) {
958 cfg.html = this.html;
962 if (this.fasize > 1) {
963 fasize = ' fa-' + this.fasize + 'x';
965 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
970 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
989 * @class Roo.bootstrap.Container
990 * @extends Roo.bootstrap.Component
991 * Bootstrap Container class
992 * @cfg {Boolean} jumbotron is it a jumbotron element
993 * @cfg {String} html content of element
994 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
995 * @cfg {String} panel (primary|success|info|warning|danger) render as panel - type - primary/success.....
996 * @cfg {String} header content of header (for panel)
997 * @cfg {String} footer content of footer (for panel)
998 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
999 * @cfg {String} tag (header|aside|section) type of HTML tag.
1000 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1001 * @cfg {String} fa font awesome icon
1002 * @cfg {String} icon (info-sign|check|...) glyphicon name
1003 * @cfg {Boolean} hidden (true|false) hide the element
1004 * @cfg {Boolean} expandable (true|false) default false
1005 * @cfg {Boolean} expanded (true|false) default true
1006 * @cfg {String} rheader contet on the right of header
1007 * @cfg {Boolean} clickable (true|false) default false
1011 * Create a new Container
1012 * @param {Object} config The config object
1015 Roo.bootstrap.Container = function(config){
1016 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1022 * After the panel has been expand
1024 * @param {Roo.bootstrap.Container} this
1029 * After the panel has been collapsed
1031 * @param {Roo.bootstrap.Container} this
1036 * When a element is chick
1037 * @param {Roo.bootstrap.Container} this
1038 * @param {Roo.EventObject} e
1044 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1062 getChildContainer : function() {
1068 if (this.panel.length) {
1069 return this.el.select('.panel-body',true).first();
1076 getAutoCreate : function(){
1079 tag : this.tag || 'div',
1083 if (this.jumbotron) {
1084 cfg.cls = 'jumbotron';
1089 // - this is applied by the parent..
1091 // cfg.cls = this.cls + '';
1094 if (this.sticky.length) {
1096 var bd = Roo.get(document.body);
1097 if (!bd.hasClass('bootstrap-sticky')) {
1098 bd.addClass('bootstrap-sticky');
1099 Roo.select('html',true).setStyle('height', '100%');
1102 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1106 if (this.well.length) {
1107 switch (this.well) {
1110 cfg.cls +=' well well-' +this.well;
1119 cfg.cls += ' hidden';
1123 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1124 cfg.cls +=' alert alert-' + this.alert;
1129 if (this.panel.length) {
1130 cfg.cls += ' panel panel-' + this.panel;
1132 if (this.header.length) {
1136 if(this.expandable){
1138 cfg.cls = cfg.cls + ' expandable';
1142 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1150 cls : 'panel-title',
1151 html : (this.expandable ? ' ' : '') + this.header
1155 cls: 'panel-header-right',
1161 cls : 'panel-heading',
1162 style : this.expandable ? 'cursor: pointer' : '',
1170 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1175 if (this.footer.length) {
1177 cls : 'panel-footer',
1186 body.html = this.html || cfg.html;
1187 // prefix with the icons..
1189 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1192 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1197 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1198 cfg.cls = 'container';
1204 initEvents: function()
1206 if(this.expandable){
1207 var headerEl = this.headerEl();
1210 headerEl.on('click', this.onToggleClick, this);
1215 this.el.on('click', this.onClick, this);
1220 onToggleClick : function()
1222 var headerEl = this.headerEl();
1238 if(this.fireEvent('expand', this)) {
1240 this.expanded = true;
1242 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1244 this.el.select('.panel-body',true).first().removeClass('hide');
1246 var toggleEl = this.toggleEl();
1252 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1257 collapse : function()
1259 if(this.fireEvent('collapse', this)) {
1261 this.expanded = false;
1263 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1264 this.el.select('.panel-body',true).first().addClass('hide');
1266 var toggleEl = this.toggleEl();
1272 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1276 toggleEl : function()
1278 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1282 return this.el.select('.panel-heading .fa',true).first();
1285 headerEl : function()
1287 if(!this.el || !this.panel.length || !this.header.length){
1291 return this.el.select('.panel-heading',true).first()
1294 titleEl : function()
1296 if(!this.el || !this.panel.length || !this.header.length){
1300 return this.el.select('.panel-title',true).first();
1303 setTitle : function(v)
1305 var titleEl = this.titleEl();
1311 titleEl.dom.innerHTML = v;
1314 getTitle : function()
1317 var titleEl = this.titleEl();
1323 return titleEl.dom.innerHTML;
1326 setRightTitle : function(v)
1328 var t = this.el.select('.panel-header-right',true).first();
1334 t.dom.innerHTML = v;
1337 onClick : function(e)
1341 this.fireEvent('click', this, e);
1355 * @class Roo.bootstrap.Img
1356 * @extends Roo.bootstrap.Component
1357 * Bootstrap Img class
1358 * @cfg {Boolean} imgResponsive false | true
1359 * @cfg {String} border rounded | circle | thumbnail
1360 * @cfg {String} src image source
1361 * @cfg {String} alt image alternative text
1362 * @cfg {String} href a tag href
1363 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1364 * @cfg {String} xsUrl xs image source
1365 * @cfg {String} smUrl sm image source
1366 * @cfg {String} mdUrl md image source
1367 * @cfg {String} lgUrl lg image source
1370 * Create a new Input
1371 * @param {Object} config The config object
1374 Roo.bootstrap.Img = function(config){
1375 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1381 * The img click event for the img.
1382 * @param {Roo.EventObject} e
1388 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1390 imgResponsive: true,
1400 getAutoCreate : function()
1402 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1403 return this.createSingleImg();
1408 cls: 'roo-image-responsive-group',
1413 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1415 if(!_this[size + 'Url']){
1421 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1422 html: _this.html || cfg.html,
1423 src: _this[size + 'Url']
1426 img.cls += ' roo-image-responsive-' + size;
1428 var s = ['xs', 'sm', 'md', 'lg'];
1430 s.splice(s.indexOf(size), 1);
1432 Roo.each(s, function(ss){
1433 img.cls += ' hidden-' + ss;
1436 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1437 cfg.cls += ' img-' + _this.border;
1441 cfg.alt = _this.alt;
1454 a.target = _this.target;
1458 cfg.cn.push((_this.href) ? a : img);
1465 createSingleImg : function()
1469 cls: (this.imgResponsive) ? 'img-responsive' : '',
1471 src : 'about:blank' // just incase src get's set to undefined?!?
1474 cfg.html = this.html || cfg.html;
1476 cfg.src = this.src || cfg.src;
1478 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1479 cfg.cls += ' img-' + this.border;
1496 a.target = this.target;
1501 return (this.href) ? a : cfg;
1504 initEvents: function()
1507 this.el.on('click', this.onClick, this);
1512 onClick : function(e)
1514 Roo.log('img onclick');
1515 this.fireEvent('click', this, e);
1529 * @class Roo.bootstrap.Link
1530 * @extends Roo.bootstrap.Component
1531 * Bootstrap Link Class
1532 * @cfg {String} alt image alternative text
1533 * @cfg {String} href a tag href
1534 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1535 * @cfg {String} html the content of the link.
1536 * @cfg {String} anchor name for the anchor link
1537 * @cfg {String} fa - favicon
1539 * @cfg {Boolean} preventDefault (true | false) default false
1543 * Create a new Input
1544 * @param {Object} config The config object
1547 Roo.bootstrap.Link = function(config){
1548 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1554 * The img click event for the img.
1555 * @param {Roo.EventObject} e
1561 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1565 preventDefault: false,
1571 getAutoCreate : function()
1573 var html = this.html || '';
1575 if (this.fa !== false) {
1576 html = '<i class="fa fa-' + this.fa + '"></i>';
1581 // anchor's do not require html/href...
1582 if (this.anchor === false) {
1584 cfg.href = this.href || '#';
1586 cfg.name = this.anchor;
1587 if (this.html !== false || this.fa !== false) {
1590 if (this.href !== false) {
1591 cfg.href = this.href;
1595 if(this.alt !== false){
1600 if(this.target !== false) {
1601 cfg.target = this.target;
1607 initEvents: function() {
1609 if(!this.href || this.preventDefault){
1610 this.el.on('click', this.onClick, this);
1614 onClick : function(e)
1616 if(this.preventDefault){
1619 //Roo.log('img onclick');
1620 this.fireEvent('click', this, e);
1633 * @class Roo.bootstrap.Header
1634 * @extends Roo.bootstrap.Component
1635 * Bootstrap Header class
1636 * @cfg {String} html content of header
1637 * @cfg {Number} level (1|2|3|4|5|6) default 1
1640 * Create a new Header
1641 * @param {Object} config The config object
1645 Roo.bootstrap.Header = function(config){
1646 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1649 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1657 getAutoCreate : function(){
1662 tag: 'h' + (1 *this.level),
1663 html: this.html || ''
1675 * Ext JS Library 1.1.1
1676 * Copyright(c) 2006-2007, Ext JS, LLC.
1678 * Originally Released Under LGPL - original licence link has changed is not relivant.
1681 * <script type="text/javascript">
1685 * @class Roo.bootstrap.MenuMgr
1686 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1689 Roo.bootstrap.MenuMgr = function(){
1690 var menus, active, groups = {}, attached = false, lastShow = new Date();
1692 // private - called when first menu is created
1695 active = new Roo.util.MixedCollection();
1696 Roo.get(document).addKeyListener(27, function(){
1697 if(active.length > 0){
1705 if(active && active.length > 0){
1706 var c = active.clone();
1716 if(active.length < 1){
1717 Roo.get(document).un("mouseup", onMouseDown);
1725 var last = active.last();
1726 lastShow = new Date();
1729 Roo.get(document).on("mouseup", onMouseDown);
1734 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1735 m.parentMenu.activeChild = m;
1736 }else if(last && last.isVisible()){
1737 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1742 function onBeforeHide(m){
1744 m.activeChild.hide();
1746 if(m.autoHideTimer){
1747 clearTimeout(m.autoHideTimer);
1748 delete m.autoHideTimer;
1753 function onBeforeShow(m){
1754 var pm = m.parentMenu;
1755 if(!pm && !m.allowOtherMenus){
1757 }else if(pm && pm.activeChild && active != m){
1758 pm.activeChild.hide();
1762 // private this should really trigger on mouseup..
1763 function onMouseDown(e){
1764 Roo.log("on Mouse Up");
1766 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1767 Roo.log("MenuManager hideAll");
1776 function onBeforeCheck(mi, state){
1778 var g = groups[mi.group];
1779 for(var i = 0, l = g.length; i < l; i++){
1781 g[i].setChecked(false);
1790 * Hides all menus that are currently visible
1792 hideAll : function(){
1797 register : function(menu){
1801 menus[menu.id] = menu;
1802 menu.on("beforehide", onBeforeHide);
1803 menu.on("hide", onHide);
1804 menu.on("beforeshow", onBeforeShow);
1805 menu.on("show", onShow);
1807 if(g && menu.events["checkchange"]){
1811 groups[g].push(menu);
1812 menu.on("checkchange", onCheck);
1817 * Returns a {@link Roo.menu.Menu} object
1818 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1819 * be used to generate and return a new Menu instance.
1821 get : function(menu){
1822 if(typeof menu == "string"){ // menu id
1824 }else if(menu.events){ // menu instance
1827 /*else if(typeof menu.length == 'number'){ // array of menu items?
1828 return new Roo.bootstrap.Menu({items:menu});
1829 }else{ // otherwise, must be a config
1830 return new Roo.bootstrap.Menu(menu);
1837 unregister : function(menu){
1838 delete menus[menu.id];
1839 menu.un("beforehide", onBeforeHide);
1840 menu.un("hide", onHide);
1841 menu.un("beforeshow", onBeforeShow);
1842 menu.un("show", onShow);
1844 if(g && menu.events["checkchange"]){
1845 groups[g].remove(menu);
1846 menu.un("checkchange", onCheck);
1851 registerCheckable : function(menuItem){
1852 var g = menuItem.group;
1857 groups[g].push(menuItem);
1858 menuItem.on("beforecheckchange", onBeforeCheck);
1863 unregisterCheckable : function(menuItem){
1864 var g = menuItem.group;
1866 groups[g].remove(menuItem);
1867 menuItem.un("beforecheckchange", onBeforeCheck);
1879 * @class Roo.bootstrap.Menu
1880 * @extends Roo.bootstrap.Component
1881 * Bootstrap Menu class - container for MenuItems
1882 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1883 * @cfg {bool} hidden if the menu should be hidden when rendered.
1884 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1885 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1889 * @param {Object} config The config object
1893 Roo.bootstrap.Menu = function(config){
1894 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1895 if (this.registerMenu && this.type != 'treeview') {
1896 Roo.bootstrap.MenuMgr.register(this);
1901 * Fires before this menu is displayed
1902 * @param {Roo.menu.Menu} this
1907 * Fires before this menu is hidden
1908 * @param {Roo.menu.Menu} this
1913 * Fires after this menu is displayed
1914 * @param {Roo.menu.Menu} this
1919 * Fires after this menu is hidden
1920 * @param {Roo.menu.Menu} this
1925 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1926 * @param {Roo.menu.Menu} this
1927 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1928 * @param {Roo.EventObject} e
1933 * Fires when the mouse is hovering over this menu
1934 * @param {Roo.menu.Menu} this
1935 * @param {Roo.EventObject} e
1936 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1941 * Fires when the mouse exits this menu
1942 * @param {Roo.menu.Menu} this
1943 * @param {Roo.EventObject} e
1944 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1949 * Fires when a menu item contained in this menu is clicked
1950 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1951 * @param {Roo.EventObject} e
1955 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1958 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1962 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1965 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1967 registerMenu : true,
1969 menuItems :false, // stores the menu items..
1979 getChildContainer : function() {
1983 getAutoCreate : function(){
1985 //if (['right'].indexOf(this.align)!==-1) {
1986 // cfg.cn[1].cls += ' pull-right'
1992 cls : 'dropdown-menu' ,
1993 style : 'z-index:1000'
1997 if (this.type === 'submenu') {
1998 cfg.cls = 'submenu active';
2000 if (this.type === 'treeview') {
2001 cfg.cls = 'treeview-menu';
2006 initEvents : function() {
2008 // Roo.log("ADD event");
2009 // Roo.log(this.triggerEl.dom);
2011 this.triggerEl.on('click', this.onTriggerClick, this);
2013 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2015 this.triggerEl.addClass('dropdown-toggle');
2018 this.el.on('touchstart' , this.onTouch, this);
2020 this.el.on('click' , this.onClick, this);
2022 this.el.on("mouseover", this.onMouseOver, this);
2023 this.el.on("mouseout", this.onMouseOut, this);
2027 findTargetItem : function(e)
2029 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2033 //Roo.log(t); Roo.log(t.id);
2035 //Roo.log(this.menuitems);
2036 return this.menuitems.get(t.id);
2038 //return this.items.get(t.menuItemId);
2044 onTouch : function(e)
2046 Roo.log("menu.onTouch");
2047 //e.stopEvent(); this make the user popdown broken
2051 onClick : function(e)
2053 Roo.log("menu.onClick");
2055 var t = this.findTargetItem(e);
2056 if(!t || t.isContainer){
2061 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2062 if(t == this.activeItem && t.shouldDeactivate(e)){
2063 this.activeItem.deactivate();
2064 delete this.activeItem;
2068 this.setActiveItem(t, true);
2076 Roo.log('pass click event');
2080 this.fireEvent("click", this, t, e);
2084 (function() { _this.hide(); }).defer(500);
2087 onMouseOver : function(e){
2088 var t = this.findTargetItem(e);
2091 // if(t.canActivate && !t.disabled){
2092 // this.setActiveItem(t, true);
2096 this.fireEvent("mouseover", this, e, t);
2098 isVisible : function(){
2099 return !this.hidden;
2101 onMouseOut : function(e){
2102 var t = this.findTargetItem(e);
2105 // if(t == this.activeItem && t.shouldDeactivate(e)){
2106 // this.activeItem.deactivate();
2107 // delete this.activeItem;
2110 this.fireEvent("mouseout", this, e, t);
2115 * Displays this menu relative to another element
2116 * @param {String/HTMLElement/Roo.Element} element The element to align to
2117 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2118 * the element (defaults to this.defaultAlign)
2119 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2121 show : function(el, pos, parentMenu){
2122 this.parentMenu = parentMenu;
2126 this.fireEvent("beforeshow", this);
2127 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2130 * Displays this menu at a specific xy position
2131 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2132 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2134 showAt : function(xy, parentMenu, /* private: */_e){
2135 this.parentMenu = parentMenu;
2140 this.fireEvent("beforeshow", this);
2141 //xy = this.el.adjustForConstraints(xy);
2145 this.hideMenuItems();
2146 this.hidden = false;
2147 this.triggerEl.addClass('open');
2149 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2150 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2153 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2158 this.fireEvent("show", this);
2164 this.doFocus.defer(50, this);
2168 doFocus : function(){
2170 this.focusEl.focus();
2175 * Hides this menu and optionally all parent menus
2176 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2178 hide : function(deep)
2181 this.hideMenuItems();
2182 if(this.el && this.isVisible()){
2183 this.fireEvent("beforehide", this);
2184 if(this.activeItem){
2185 this.activeItem.deactivate();
2186 this.activeItem = null;
2188 this.triggerEl.removeClass('open');;
2190 this.fireEvent("hide", this);
2192 if(deep === true && this.parentMenu){
2193 this.parentMenu.hide(true);
2197 onTriggerClick : function(e)
2199 Roo.log('trigger click');
2201 var target = e.getTarget();
2203 Roo.log(target.nodeName.toLowerCase());
2205 if(target.nodeName.toLowerCase() === 'i'){
2211 onTriggerPress : function(e)
2213 Roo.log('trigger press');
2214 //Roo.log(e.getTarget());
2215 // Roo.log(this.triggerEl.dom);
2217 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2218 var pel = Roo.get(e.getTarget());
2219 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2220 Roo.log('is treeview or dropdown?');
2224 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2228 if (this.isVisible()) {
2233 this.show(this.triggerEl, false, false);
2236 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2243 hideMenuItems : function()
2245 Roo.log("hide Menu Items");
2249 //$(backdrop).remove()
2250 this.el.select('.open',true).each(function(aa) {
2252 aa.removeClass('open');
2253 //var parent = getParent($(this))
2254 //var relatedTarget = { relatedTarget: this }
2256 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2257 //if (e.isDefaultPrevented()) return
2258 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2261 addxtypeChild : function (tree, cntr) {
2262 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2264 this.menuitems.add(comp);
2285 * @class Roo.bootstrap.MenuItem
2286 * @extends Roo.bootstrap.Component
2287 * Bootstrap MenuItem class
2288 * @cfg {String} html the menu label
2289 * @cfg {String} href the link
2290 * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2291 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2292 * @cfg {Boolean} active used on sidebars to highlight active itesm
2293 * @cfg {String} fa favicon to show on left of menu item.
2294 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2298 * Create a new MenuItem
2299 * @param {Object} config The config object
2303 Roo.bootstrap.MenuItem = function(config){
2304 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2309 * The raw click event for the entire grid.
2310 * @param {Roo.bootstrap.MenuItem} this
2311 * @param {Roo.EventObject} e
2317 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2321 preventDefault: true,
2322 isContainer : false,
2326 getAutoCreate : function(){
2328 if(this.isContainer){
2331 cls: 'dropdown-menu-item'
2345 if (this.fa !== false) {
2348 cls : 'fa fa-' + this.fa
2357 cls: 'dropdown-menu-item',
2360 if (this.parent().type == 'treeview') {
2361 cfg.cls = 'treeview-menu';
2364 cfg.cls += ' active';
2369 anc.href = this.href || cfg.cn[0].href ;
2370 ctag.html = this.html || cfg.cn[0].html ;
2374 initEvents: function()
2376 if (this.parent().type == 'treeview') {
2377 this.el.select('a').on('click', this.onClick, this);
2380 this.menu.parentType = this.xtype;
2381 this.menu.triggerEl = this.el;
2382 this.menu = this.addxtype(Roo.apply({}, this.menu));
2386 onClick : function(e)
2388 Roo.log('item on click ');
2389 //if(this.preventDefault){
2390 // e.preventDefault();
2392 //this.parent().hideMenuItems();
2394 this.fireEvent('click', this, e);
2413 * @class Roo.bootstrap.MenuSeparator
2414 * @extends Roo.bootstrap.Component
2415 * Bootstrap MenuSeparator class
2418 * Create a new MenuItem
2419 * @param {Object} config The config object
2423 Roo.bootstrap.MenuSeparator = function(config){
2424 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2427 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2429 getAutoCreate : function(){
2448 * @class Roo.bootstrap.Modal
2449 * @extends Roo.bootstrap.Component
2450 * Bootstrap Modal class
2451 * @cfg {String} title Title of dialog
2452 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2453 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2454 * @cfg {Boolean} specificTitle default false
2455 * @cfg {Array} buttons Array of buttons or standard button set..
2456 * @cfg {String} buttonPosition (left|right|center) default right
2457 * @cfg {Boolean} animate default true
2458 * @cfg {Boolean} allow_close default true
2459 * @cfg {Boolean} fitwindow default false
2460 * @cfg {String} size (sm|lg) default empty
2464 * Create a new Modal Dialog
2465 * @param {Object} config The config object
2468 Roo.bootstrap.Modal = function(config){
2469 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2474 * The raw btnclick event for the button
2475 * @param {Roo.EventObject} e
2479 this.buttons = this.buttons || [];
2482 this.tmpl = Roo.factory(this.tmpl);
2487 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2489 title : 'test dialog',
2499 specificTitle: false,
2501 buttonPosition: 'right',
2520 onRender : function(ct, position)
2522 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2525 var cfg = Roo.apply({}, this.getAutoCreate());
2528 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2530 //if (!cfg.name.length) {
2534 cfg.cls += ' ' + this.cls;
2537 cfg.style = this.style;
2539 this.el = Roo.get(document.body).createChild(cfg, position);
2541 //var type = this.el.dom.type;
2544 if(this.tabIndex !== undefined){
2545 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2548 this.dialogEl = this.el.select('.modal-dialog',true).first();
2549 this.bodyEl = this.el.select('.modal-body',true).first();
2550 this.closeEl = this.el.select('.modal-header .close', true).first();
2551 this.footerEl = this.el.select('.modal-footer',true).first();
2552 this.titleEl = this.el.select('.modal-title',true).first();
2556 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2557 this.maskEl.enableDisplayMode("block");
2559 //this.el.addClass("x-dlg-modal");
2561 if (this.buttons.length) {
2562 Roo.each(this.buttons, function(bb) {
2563 var b = Roo.apply({}, bb);
2564 b.xns = b.xns || Roo.bootstrap;
2565 b.xtype = b.xtype || 'Button';
2566 if (typeof(b.listeners) == 'undefined') {
2567 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2570 var btn = Roo.factory(b);
2572 btn.render(this.el.select('.modal-footer div').first());
2576 // render the children.
2579 if(typeof(this.items) != 'undefined'){
2580 var items = this.items;
2583 for(var i =0;i < items.length;i++) {
2584 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2588 this.items = nitems;
2590 // where are these used - they used to be body/close/footer
2594 //this.el.addClass([this.fieldClass, this.cls]);
2598 getAutoCreate : function(){
2603 html : this.html || ''
2608 cls : 'modal-title',
2612 if(this.specificTitle){
2618 if (this.allow_close) {
2630 if(this.size.length){
2631 size = 'modal-' + this.size;
2636 style : 'display: none',
2639 cls: "modal-dialog " + size,
2642 cls : "modal-content",
2645 cls : 'modal-header',
2650 cls : 'modal-footer',
2654 cls: 'btn-' + this.buttonPosition
2671 modal.cls += ' fade';
2677 getChildContainer : function() {
2682 getButtonContainer : function() {
2683 return this.el.select('.modal-footer div',true).first();
2686 initEvents : function()
2688 if (this.allow_close) {
2689 this.closeEl.on('click', this.hide, this);
2691 Roo.EventManager.onWindowResize(this.resize, this, true);
2698 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2699 if (this.fitwindow) {
2700 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2701 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2706 setSize : function(w,h)
2716 if (!this.rendered) {
2720 this.el.setStyle('display', 'block');
2722 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2725 this.el.addClass('in');
2728 this.el.addClass('in');
2732 // not sure how we can show data in here..
2734 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2737 Roo.get(document.body).addClass("x-body-masked");
2738 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2740 this.el.setStyle('zIndex', '10001');
2742 this.fireEvent('show', this);
2747 this.items.forEach( function(e) {
2748 e.layout ? e.layout() : false;
2757 Roo.get(document.body).removeClass("x-body-masked");
2758 this.el.removeClass('in');
2759 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2761 if(this.animate){ // why
2763 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2765 this.el.setStyle('display', 'none');
2768 this.fireEvent('hide', this);
2771 addButton : function(str, cb)
2775 var b = Roo.apply({}, { html : str } );
2776 b.xns = b.xns || Roo.bootstrap;
2777 b.xtype = b.xtype || 'Button';
2778 if (typeof(b.listeners) == 'undefined') {
2779 b.listeners = { click : cb.createDelegate(this) };
2782 var btn = Roo.factory(b);
2784 btn.render(this.el.select('.modal-footer div').first());
2790 setDefaultButton : function(btn)
2792 //this.el.select('.modal-footer').()
2796 resizeTo: function(w,h)
2800 this.dialogEl.setWidth(w);
2801 if (this.diff === false) {
2802 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2805 this.bodyEl.setHeight(h-this.diff);
2809 setContentSize : function(w, h)
2813 onButtonClick: function(btn,e)
2816 this.fireEvent('btnclick', btn.name, e);
2819 * Set the title of the Dialog
2820 * @param {String} str new Title
2822 setTitle: function(str) {
2823 this.titleEl.dom.innerHTML = str;
2826 * Set the body of the Dialog
2827 * @param {String} str new Title
2829 setBody: function(str) {
2830 this.bodyEl.dom.innerHTML = str;
2833 * Set the body of the Dialog using the template
2834 * @param {Obj} data - apply this data to the template and replace the body contents.
2836 applyBody: function(obj)
2839 Roo.log("Error - using apply Body without a template");
2842 this.tmpl.overwrite(this.bodyEl, obj);
2848 Roo.apply(Roo.bootstrap.Modal, {
2850 * Button config that displays a single OK button
2859 * Button config that displays Yes and No buttons
2875 * Button config that displays OK and Cancel buttons
2890 * Button config that displays Yes, No and Cancel buttons
2913 * messagebox - can be used as a replace
2917 * @class Roo.MessageBox
2918 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2922 Roo.Msg.alert('Status', 'Changes saved successfully.');
2924 // Prompt for user data:
2925 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2927 // process text value...
2931 // Show a dialog using config options:
2933 title:'Save Changes?',
2934 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2935 buttons: Roo.Msg.YESNOCANCEL,
2942 Roo.bootstrap.MessageBox = function(){
2943 var dlg, opt, mask, waitTimer;
2944 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2945 var buttons, activeTextEl, bwidth;
2949 var handleButton = function(button){
2951 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2955 var handleHide = function(){
2957 dlg.el.removeClass(opt.cls);
2960 // Roo.TaskMgr.stop(waitTimer);
2961 // waitTimer = null;
2966 var updateButtons = function(b){
2969 buttons["ok"].hide();
2970 buttons["cancel"].hide();
2971 buttons["yes"].hide();
2972 buttons["no"].hide();
2973 //dlg.footer.dom.style.display = 'none';
2976 dlg.footerEl.dom.style.display = '';
2977 for(var k in buttons){
2978 if(typeof buttons[k] != "function"){
2981 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2982 width += buttons[k].el.getWidth()+15;
2992 var handleEsc = function(d, k, e){
2993 if(opt && opt.closable !== false){
3003 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3004 * @return {Roo.BasicDialog} The BasicDialog element
3006 getDialog : function(){
3008 dlg = new Roo.bootstrap.Modal( {
3011 //constraintoviewport:false,
3013 //collapsible : false,
3018 //buttonAlign:"center",
3019 closeClick : function(){
3020 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3023 handleButton("cancel");
3028 dlg.on("hide", handleHide);
3030 //dlg.addKeyListener(27, handleEsc);
3032 this.buttons = buttons;
3033 var bt = this.buttonText;
3034 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3035 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3036 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3037 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3039 bodyEl = dlg.bodyEl.createChild({
3041 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3042 '<textarea class="roo-mb-textarea"></textarea>' +
3043 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3045 msgEl = bodyEl.dom.firstChild;
3046 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3047 textboxEl.enableDisplayMode();
3048 textboxEl.addKeyListener([10,13], function(){
3049 if(dlg.isVisible() && opt && opt.buttons){
3052 }else if(opt.buttons.yes){
3053 handleButton("yes");
3057 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3058 textareaEl.enableDisplayMode();
3059 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3060 progressEl.enableDisplayMode();
3061 var pf = progressEl.dom.firstChild;
3063 pp = Roo.get(pf.firstChild);
3064 pp.setHeight(pf.offsetHeight);
3072 * Updates the message box body text
3073 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3074 * the XHTML-compliant non-breaking space character '&#160;')
3075 * @return {Roo.MessageBox} This message box
3077 updateText : function(text){
3078 if(!dlg.isVisible() && !opt.width){
3079 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3081 msgEl.innerHTML = text || ' ';
3083 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3084 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3086 Math.min(opt.width || cw , this.maxWidth),
3087 Math.max(opt.minWidth || this.minWidth, bwidth)
3090 activeTextEl.setWidth(w);
3092 if(dlg.isVisible()){
3093 dlg.fixedcenter = false;
3095 // to big, make it scroll. = But as usual stupid IE does not support
3098 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3099 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3100 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3102 bodyEl.dom.style.height = '';
3103 bodyEl.dom.style.overflowY = '';
3106 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3108 bodyEl.dom.style.overflowX = '';
3111 dlg.setContentSize(w, bodyEl.getHeight());
3112 if(dlg.isVisible()){
3113 dlg.fixedcenter = true;
3119 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3120 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3121 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3122 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3123 * @return {Roo.MessageBox} This message box
3125 updateProgress : function(value, text){
3127 this.updateText(text);
3129 if (pp) { // weird bug on my firefox - for some reason this is not defined
3130 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3136 * Returns true if the message box is currently displayed
3137 * @return {Boolean} True if the message box is visible, else false
3139 isVisible : function(){
3140 return dlg && dlg.isVisible();
3144 * Hides the message box if it is displayed
3147 if(this.isVisible()){
3153 * Displays a new message box, or reinitializes an existing message box, based on the config options
3154 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3155 * The following config object properties are supported:
3157 Property Type Description
3158 ---------- --------------- ------------------------------------------------------------------------------------
3159 animEl String/Element An id or Element from which the message box should animate as it opens and
3160 closes (defaults to undefined)
3161 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3162 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3163 closable Boolean False to hide the top-right close button (defaults to true). Note that
3164 progress and wait dialogs will ignore this property and always hide the
3165 close button as they can only be closed programmatically.
3166 cls String A custom CSS class to apply to the message box element
3167 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3168 displayed (defaults to 75)
3169 fn Function A callback function to execute after closing the dialog. The arguments to the
3170 function will be btn (the name of the button that was clicked, if applicable,
3171 e.g. "ok"), and text (the value of the active text field, if applicable).
3172 Progress and wait dialogs will ignore this option since they do not respond to
3173 user actions and can only be closed programmatically, so any required function
3174 should be called by the same code after it closes the dialog.
3175 icon String A CSS class that provides a background image to be used as an icon for
3176 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3177 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3178 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3179 modal Boolean False to allow user interaction with the page while the message box is
3180 displayed (defaults to true)
3181 msg String A string that will replace the existing message box body text (defaults
3182 to the XHTML-compliant non-breaking space character ' ')
3183 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3184 progress Boolean True to display a progress bar (defaults to false)
3185 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3186 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3187 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3188 title String The title text
3189 value String The string value to set into the active textbox element if displayed
3190 wait Boolean True to display a progress bar (defaults to false)
3191 width Number The width of the dialog in pixels
3198 msg: 'Please enter your address:',
3200 buttons: Roo.MessageBox.OKCANCEL,
3203 animEl: 'addAddressBtn'
3206 * @param {Object} config Configuration options
3207 * @return {Roo.MessageBox} This message box
3209 show : function(options)
3212 // this causes nightmares if you show one dialog after another
3213 // especially on callbacks..
3215 if(this.isVisible()){
3218 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3219 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3220 Roo.log("New Dialog Message:" + options.msg )
3221 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3222 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3225 var d = this.getDialog();
3227 d.setTitle(opt.title || " ");
3228 d.closeEl.setDisplayed(opt.closable !== false);
3229 activeTextEl = textboxEl;
3230 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3235 textareaEl.setHeight(typeof opt.multiline == "number" ?
3236 opt.multiline : this.defaultTextHeight);
3237 activeTextEl = textareaEl;
3246 progressEl.setDisplayed(opt.progress === true);
3247 this.updateProgress(0);
3248 activeTextEl.dom.value = opt.value || "";
3250 dlg.setDefaultButton(activeTextEl);
3252 var bs = opt.buttons;
3256 }else if(bs && bs.yes){
3257 db = buttons["yes"];
3259 dlg.setDefaultButton(db);
3261 bwidth = updateButtons(opt.buttons);
3262 this.updateText(opt.msg);
3264 d.el.addClass(opt.cls);
3266 d.proxyDrag = opt.proxyDrag === true;
3267 d.modal = opt.modal !== false;
3268 d.mask = opt.modal !== false ? mask : false;
3270 // force it to the end of the z-index stack so it gets a cursor in FF
3271 document.body.appendChild(dlg.el.dom);
3272 d.animateTarget = null;
3273 d.show(options.animEl);
3279 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3280 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3281 * and closing the message box when the process is complete.
3282 * @param {String} title The title bar text
3283 * @param {String} msg The message box body text
3284 * @return {Roo.MessageBox} This message box
3286 progress : function(title, msg){
3293 minWidth: this.minProgressWidth,
3300 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3301 * If a callback function is passed it will be called after the user clicks the button, and the
3302 * id of the button that was clicked will be passed as the only parameter to the callback
3303 * (could also be the top-right close button).
3304 * @param {String} title The title bar text
3305 * @param {String} msg The message box body text
3306 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3307 * @param {Object} scope (optional) The scope of the callback function
3308 * @return {Roo.MessageBox} This message box
3310 alert : function(title, msg, fn, scope){
3323 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3324 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3325 * You are responsible for closing the message box when the process is complete.
3326 * @param {String} msg The message box body text
3327 * @param {String} title (optional) The title bar text
3328 * @return {Roo.MessageBox} This message box
3330 wait : function(msg, title){
3341 waitTimer = Roo.TaskMgr.start({
3343 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3351 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3352 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3353 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3354 * @param {String} title The title bar text
3355 * @param {String} msg The message box body text
3356 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3357 * @param {Object} scope (optional) The scope of the callback function
3358 * @return {Roo.MessageBox} This message box
3360 confirm : function(title, msg, fn, scope){
3364 buttons: this.YESNO,
3373 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3374 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3375 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3376 * (could also be the top-right close button) and the text that was entered will be passed as the two
3377 * parameters to the callback.
3378 * @param {String} title The title bar text
3379 * @param {String} msg The message box body text
3380 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3381 * @param {Object} scope (optional) The scope of the callback function
3382 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3383 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3384 * @return {Roo.MessageBox} This message box
3386 prompt : function(title, msg, fn, scope, multiline){
3390 buttons: this.OKCANCEL,
3395 multiline: multiline,
3402 * Button config that displays a single OK button
3407 * Button config that displays Yes and No buttons
3410 YESNO : {yes:true, no:true},
3412 * Button config that displays OK and Cancel buttons
3415 OKCANCEL : {ok:true, cancel:true},
3417 * Button config that displays Yes, No and Cancel buttons
3420 YESNOCANCEL : {yes:true, no:true, cancel:true},
3423 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3426 defaultTextHeight : 75,
3428 * The maximum width in pixels of the message box (defaults to 600)
3433 * The minimum width in pixels of the message box (defaults to 100)
3438 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3439 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3442 minProgressWidth : 250,
3444 * An object containing the default button text strings that can be overriden for localized language support.
3445 * Supported properties are: ok, cancel, yes and no.
3446 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3459 * Shorthand for {@link Roo.MessageBox}
3461 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3462 Roo.Msg = Roo.Msg || Roo.MessageBox;
3471 * @class Roo.bootstrap.Navbar
3472 * @extends Roo.bootstrap.Component
3473 * Bootstrap Navbar class
3476 * Create a new Navbar
3477 * @param {Object} config The config object
3481 Roo.bootstrap.Navbar = function(config){
3482 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3486 * @event beforetoggle
3487 * Fire before toggle the menu
3488 * @param {Roo.EventObject} e
3490 "beforetoggle" : true
3494 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3503 getAutoCreate : function(){
3506 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3510 initEvents :function ()
3512 //Roo.log(this.el.select('.navbar-toggle',true));
3513 this.el.select('.navbar-toggle',true).on('click', function() {
3514 if(this.fireEvent('beforetoggle', this) !== false){
3515 this.el.select('.navbar-collapse',true).toggleClass('in');
3525 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3527 var size = this.el.getSize();
3528 this.maskEl.setSize(size.width, size.height);
3529 this.maskEl.enableDisplayMode("block");
3538 getChildContainer : function()
3540 if (this.el.select('.collapse').getCount()) {
3541 return this.el.select('.collapse',true).first();
3574 * @class Roo.bootstrap.NavSimplebar
3575 * @extends Roo.bootstrap.Navbar
3576 * Bootstrap Sidebar class
3578 * @cfg {Boolean} inverse is inverted color
3580 * @cfg {String} type (nav | pills | tabs)
3581 * @cfg {Boolean} arrangement stacked | justified
3582 * @cfg {String} align (left | right) alignment
3584 * @cfg {Boolean} main (true|false) main nav bar? default false
3585 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3587 * @cfg {String} tag (header|footer|nav|div) default is nav
3593 * Create a new Sidebar
3594 * @param {Object} config The config object
3598 Roo.bootstrap.NavSimplebar = function(config){
3599 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3602 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3618 getAutoCreate : function(){
3622 tag : this.tag || 'div',
3635 this.type = this.type || 'nav';
3636 if (['tabs','pills'].indexOf(this.type)!==-1) {
3637 cfg.cn[0].cls += ' nav-' + this.type
3641 if (this.type!=='nav') {
3642 Roo.log('nav type must be nav/tabs/pills')
3644 cfg.cn[0].cls += ' navbar-nav'
3650 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3651 cfg.cn[0].cls += ' nav-' + this.arrangement;
3655 if (this.align === 'right') {
3656 cfg.cn[0].cls += ' navbar-right';
3660 cfg.cls += ' navbar-inverse';
3687 * @class Roo.bootstrap.NavHeaderbar
3688 * @extends Roo.bootstrap.NavSimplebar
3689 * Bootstrap Sidebar class
3691 * @cfg {String} brand what is brand
3692 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3693 * @cfg {String} brand_href href of the brand
3694 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3695 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3696 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3697 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3700 * Create a new Sidebar
3701 * @param {Object} config The config object
3705 Roo.bootstrap.NavHeaderbar = function(config){
3706 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3710 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3717 desktopCenter : false,
3720 getAutoCreate : function(){
3723 tag: this.nav || 'nav',
3730 if (this.desktopCenter) {
3731 cn.push({cls : 'container', cn : []});
3738 cls: 'navbar-header',
3743 cls: 'navbar-toggle',
3744 'data-toggle': 'collapse',
3749 html: 'Toggle navigation'
3771 cls: 'collapse navbar-collapse',
3775 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3777 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3778 cfg.cls += ' navbar-' + this.position;
3780 // tag can override this..
3782 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3785 if (this.brand !== '') {
3788 href: this.brand_href ? this.brand_href : '#',
3789 cls: 'navbar-brand',
3797 cfg.cls += ' main-nav';
3805 getHeaderChildContainer : function()
3807 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3808 return this.el.select('.navbar-header',true).first();
3811 return this.getChildContainer();
3815 initEvents : function()
3817 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3819 if (this.autohide) {
3824 Roo.get(document).on('scroll',function(e) {
3825 var ns = Roo.get(document).getScroll().top;
3826 var os = prevScroll;
3830 ft.removeClass('slideDown');
3831 ft.addClass('slideUp');
3834 ft.removeClass('slideUp');
3835 ft.addClass('slideDown');
3856 * @class Roo.bootstrap.NavSidebar
3857 * @extends Roo.bootstrap.Navbar
3858 * Bootstrap Sidebar class
3861 * Create a new Sidebar
3862 * @param {Object} config The config object
3866 Roo.bootstrap.NavSidebar = function(config){
3867 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3870 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3872 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3874 getAutoCreate : function(){
3879 cls: 'sidebar sidebar-nav'
3901 * @class Roo.bootstrap.NavGroup
3902 * @extends Roo.bootstrap.Component
3903 * Bootstrap NavGroup class
3904 * @cfg {String} align (left|right)
3905 * @cfg {Boolean} inverse
3906 * @cfg {String} type (nav|pills|tab) default nav
3907 * @cfg {String} navId - reference Id for navbar.
3911 * Create a new nav group
3912 * @param {Object} config The config object
3915 Roo.bootstrap.NavGroup = function(config){
3916 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3919 Roo.bootstrap.NavGroup.register(this);
3923 * Fires when the active item changes
3924 * @param {Roo.bootstrap.NavGroup} this
3925 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3926 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3933 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3944 getAutoCreate : function()
3946 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3953 if (['tabs','pills'].indexOf(this.type)!==-1) {
3954 cfg.cls += ' nav-' + this.type
3956 if (this.type!=='nav') {
3957 Roo.log('nav type must be nav/tabs/pills')
3959 cfg.cls += ' navbar-nav'
3962 if (this.parent().sidebar) {
3965 cls: 'dashboard-menu sidebar-menu'
3971 if (this.form === true) {
3977 if (this.align === 'right') {
3978 cfg.cls += ' navbar-right';
3980 cfg.cls += ' navbar-left';
3984 if (this.align === 'right') {
3985 cfg.cls += ' navbar-right';
3989 cfg.cls += ' navbar-inverse';
3997 * sets the active Navigation item
3998 * @param {Roo.bootstrap.NavItem} the new current navitem
4000 setActiveItem : function(item)
4003 Roo.each(this.navItems, function(v){
4008 v.setActive(false, true);
4015 item.setActive(true, true);
4016 this.fireEvent('changed', this, item, prev);
4021 * gets the active Navigation item
4022 * @return {Roo.bootstrap.NavItem} the current navitem
4024 getActive : function()
4028 Roo.each(this.navItems, function(v){
4039 indexOfNav : function()
4043 Roo.each(this.navItems, function(v,i){
4054 * adds a Navigation item
4055 * @param {Roo.bootstrap.NavItem} the navitem to add
4057 addItem : function(cfg)
4059 var cn = new Roo.bootstrap.NavItem(cfg);
4061 cn.parentId = this.id;
4062 cn.onRender(this.el, null);
4066 * register a Navigation item
4067 * @param {Roo.bootstrap.NavItem} the navitem to add
4069 register : function(item)
4071 this.navItems.push( item);
4072 item.navId = this.navId;
4077 * clear all the Navigation item
4080 clearAll : function()
4083 this.el.dom.innerHTML = '';
4086 getNavItem: function(tabId)
4089 Roo.each(this.navItems, function(e) {
4090 if (e.tabId == tabId) {
4100 setActiveNext : function()
4102 var i = this.indexOfNav(this.getActive());
4103 if (i > this.navItems.length) {
4106 this.setActiveItem(this.navItems[i+1]);
4108 setActivePrev : function()
4110 var i = this.indexOfNav(this.getActive());
4114 this.setActiveItem(this.navItems[i-1]);
4116 clearWasActive : function(except) {
4117 Roo.each(this.navItems, function(e) {
4118 if (e.tabId != except.tabId && e.was_active) {
4119 e.was_active = false;
4126 getWasActive : function ()
4129 Roo.each(this.navItems, function(e) {
4144 Roo.apply(Roo.bootstrap.NavGroup, {
4148 * register a Navigation Group
4149 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4151 register : function(navgrp)
4153 this.groups[navgrp.navId] = navgrp;
4157 * fetch a Navigation Group based on the navigation ID
4158 * @param {string} the navgroup to add
4159 * @returns {Roo.bootstrap.NavGroup} the navgroup
4161 get: function(navId) {
4162 if (typeof(this.groups[navId]) == 'undefined') {
4164 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4166 return this.groups[navId] ;
4181 * @class Roo.bootstrap.NavItem
4182 * @extends Roo.bootstrap.Component
4183 * Bootstrap Navbar.NavItem class
4184 * @cfg {String} href link to
4185 * @cfg {String} html content of button
4186 * @cfg {String} badge text inside badge
4187 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4188 * @cfg {String} glyphicon name of glyphicon
4189 * @cfg {String} icon name of font awesome icon
4190 * @cfg {Boolean} active Is item active
4191 * @cfg {Boolean} disabled Is item disabled
4193 * @cfg {Boolean} preventDefault (true | false) default false
4194 * @cfg {String} tabId the tab that this item activates.
4195 * @cfg {String} tagtype (a|span) render as a href or span?
4196 * @cfg {Boolean} animateRef (true|false) link to element default false
4199 * Create a new Navbar Item
4200 * @param {Object} config The config object
4202 Roo.bootstrap.NavItem = function(config){
4203 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4208 * The raw click event for the entire grid.
4209 * @param {Roo.EventObject} e
4214 * Fires when the active item active state changes
4215 * @param {Roo.bootstrap.NavItem} this
4216 * @param {boolean} state the new state
4222 * Fires when scroll to element
4223 * @param {Roo.bootstrap.NavItem} this
4224 * @param {Object} options
4225 * @param {Roo.EventObject} e
4233 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4241 preventDefault : false,
4248 getAutoCreate : function(){
4257 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4259 if (this.disabled) {
4260 cfg.cls += ' disabled';
4263 if (this.href || this.html || this.glyphicon || this.icon) {
4267 href : this.href || "#",
4268 html: this.html || ''
4273 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4276 if(this.glyphicon) {
4277 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4282 cfg.cn[0].html += " <span class='caret'></span>";
4286 if (this.badge !== '') {
4288 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4296 initEvents: function()
4298 if (typeof (this.menu) != 'undefined') {
4299 this.menu.parentType = this.xtype;
4300 this.menu.triggerEl = this.el;
4301 this.menu = this.addxtype(Roo.apply({}, this.menu));
4304 this.el.select('a',true).on('click', this.onClick, this);
4306 if(this.tagtype == 'span'){
4307 this.el.select('span',true).on('click', this.onClick, this);
4310 // at this point parent should be available..
4311 this.parent().register(this);
4314 onClick : function(e)
4316 if (e.getTarget('.dropdown-menu-item')) {
4317 // did you click on a menu itemm.... - then don't trigger onclick..
4322 this.preventDefault ||
4325 Roo.log("NavItem - prevent Default?");
4329 if (this.disabled) {
4333 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4334 if (tg && tg.transition) {
4335 Roo.log("waiting for the transitionend");
4341 //Roo.log("fire event clicked");
4342 if(this.fireEvent('click', this, e) === false){
4346 if(this.tagtype == 'span'){
4350 //Roo.log(this.href);
4351 var ael = this.el.select('a',true).first();
4354 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4355 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4356 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4357 return; // ignore... - it's a 'hash' to another page.
4359 Roo.log("NavItem - prevent Default?");
4361 this.scrollToElement(e);
4365 var p = this.parent();
4367 if (['tabs','pills'].indexOf(p.type)!==-1) {
4368 if (typeof(p.setActiveItem) !== 'undefined') {
4369 p.setActiveItem(this);
4373 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4374 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4375 // remove the collapsed menu expand...
4376 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4380 isActive: function () {
4383 setActive : function(state, fire, is_was_active)
4385 if (this.active && !state && this.navId) {
4386 this.was_active = true;
4387 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4389 nv.clearWasActive(this);
4393 this.active = state;
4396 this.el.removeClass('active');
4397 } else if (!this.el.hasClass('active')) {
4398 this.el.addClass('active');
4401 this.fireEvent('changed', this, state);
4404 // show a panel if it's registered and related..
4406 if (!this.navId || !this.tabId || !state || is_was_active) {
4410 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4414 var pan = tg.getPanelByName(this.tabId);
4418 // if we can not flip to new panel - go back to old nav highlight..
4419 if (false == tg.showPanel(pan)) {
4420 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4422 var onav = nv.getWasActive();
4424 onav.setActive(true, false, true);
4433 // this should not be here...
4434 setDisabled : function(state)
4436 this.disabled = state;
4438 this.el.removeClass('disabled');
4439 } else if (!this.el.hasClass('disabled')) {
4440 this.el.addClass('disabled');
4446 * Fetch the element to display the tooltip on.
4447 * @return {Roo.Element} defaults to this.el
4449 tooltipEl : function()
4451 return this.el.select('' + this.tagtype + '', true).first();
4454 scrollToElement : function(e)
4456 var c = document.body;
4459 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4461 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4462 c = document.documentElement;
4465 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4471 var o = target.calcOffsetsTo(c);
4478 this.fireEvent('scrollto', this, options, e);
4480 Roo.get(c).scrollTo('top', options.value, true);
4493 * <span> icon </span>
4494 * <span> text </span>
4495 * <span>badge </span>
4499 * @class Roo.bootstrap.NavSidebarItem
4500 * @extends Roo.bootstrap.NavItem
4501 * Bootstrap Navbar.NavSidebarItem class
4502 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4503 * {bool} open is the menu open
4505 * Create a new Navbar Button
4506 * @param {Object} config The config object
4508 Roo.bootstrap.NavSidebarItem = function(config){
4509 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4514 * The raw click event for the entire grid.
4515 * @param {Roo.EventObject} e
4520 * Fires when the active item active state changes
4521 * @param {Roo.bootstrap.NavSidebarItem} this
4522 * @param {boolean} state the new state
4530 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4532 badgeWeight : 'default',
4536 getAutoCreate : function(){
4541 href : this.href || '#',
4553 html : this.html || ''
4558 cfg.cls += ' active';
4561 if (this.disabled) {
4562 cfg.cls += ' disabled';
4565 cfg.cls += ' open x-open';
4568 if (this.glyphicon || this.icon) {
4569 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4570 a.cn.push({ tag : 'i', cls : c }) ;
4575 if (this.badge !== '') {
4577 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4581 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4582 a.cls += 'dropdown-toggle treeview' ;
4590 initEvents : function()
4592 if (typeof (this.menu) != 'undefined') {
4593 this.menu.parentType = this.xtype;
4594 this.menu.triggerEl = this.el;
4595 this.menu = this.addxtype(Roo.apply({}, this.menu));
4598 this.el.on('click', this.onClick, this);
4601 if(this.badge !== ''){
4603 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4608 onClick : function(e)
4615 if(this.preventDefault){
4619 this.fireEvent('click', this);
4622 disable : function()
4624 this.setDisabled(true);
4629 this.setDisabled(false);
4632 setDisabled : function(state)
4634 if(this.disabled == state){
4638 this.disabled = state;
4641 this.el.addClass('disabled');
4645 this.el.removeClass('disabled');
4650 setActive : function(state)
4652 if(this.active == state){
4656 this.active = state;
4659 this.el.addClass('active');
4663 this.el.removeClass('active');
4668 isActive: function ()
4673 setBadge : function(str)
4679 this.badgeEl.dom.innerHTML = str;
4696 * @class Roo.bootstrap.Row
4697 * @extends Roo.bootstrap.Component
4698 * Bootstrap Row class (contains columns...)
4702 * @param {Object} config The config object
4705 Roo.bootstrap.Row = function(config){
4706 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4709 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4711 getAutoCreate : function(){
4730 * @class Roo.bootstrap.Element
4731 * @extends Roo.bootstrap.Component
4732 * Bootstrap Element class
4733 * @cfg {String} html contents of the element
4734 * @cfg {String} tag tag of the element
4735 * @cfg {String} cls class of the element
4736 * @cfg {Boolean} preventDefault (true|false) default false
4737 * @cfg {Boolean} clickable (true|false) default false
4740 * Create a new Element
4741 * @param {Object} config The config object
4744 Roo.bootstrap.Element = function(config){
4745 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4751 * When a element is chick
4752 * @param {Roo.bootstrap.Element} this
4753 * @param {Roo.EventObject} e
4759 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4764 preventDefault: false,
4767 getAutoCreate : function(){
4778 initEvents: function()
4780 Roo.bootstrap.Element.superclass.initEvents.call(this);
4783 this.el.on('click', this.onClick, this);
4788 onClick : function(e)
4790 if(this.preventDefault){
4794 this.fireEvent('click', this, e);
4797 getValue : function()
4799 return this.el.dom.innerHTML;
4802 setValue : function(value)
4804 this.el.dom.innerHTML = value;
4819 * @class Roo.bootstrap.Pagination
4820 * @extends Roo.bootstrap.Component
4821 * Bootstrap Pagination class
4822 * @cfg {String} size xs | sm | md | lg
4823 * @cfg {Boolean} inverse false | true
4826 * Create a new Pagination
4827 * @param {Object} config The config object
4830 Roo.bootstrap.Pagination = function(config){
4831 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4834 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4840 getAutoCreate : function(){
4846 cfg.cls += ' inverse';
4852 cfg.cls += " " + this.cls;
4870 * @class Roo.bootstrap.PaginationItem
4871 * @extends Roo.bootstrap.Component
4872 * Bootstrap PaginationItem class
4873 * @cfg {String} html text
4874 * @cfg {String} href the link
4875 * @cfg {Boolean} preventDefault (true | false) default true
4876 * @cfg {Boolean} active (true | false) default false
4877 * @cfg {Boolean} disabled default false
4881 * Create a new PaginationItem
4882 * @param {Object} config The config object
4886 Roo.bootstrap.PaginationItem = function(config){
4887 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4892 * The raw click event for the entire grid.
4893 * @param {Roo.EventObject} e
4899 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4903 preventDefault: true,
4908 getAutoCreate : function(){
4914 href : this.href ? this.href : '#',
4915 html : this.html ? this.html : ''
4925 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4929 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4935 initEvents: function() {
4937 this.el.on('click', this.onClick, this);
4940 onClick : function(e)
4942 Roo.log('PaginationItem on click ');
4943 if(this.preventDefault){
4951 this.fireEvent('click', this, e);
4967 * @class Roo.bootstrap.Slider
4968 * @extends Roo.bootstrap.Component
4969 * Bootstrap Slider class
4972 * Create a new Slider
4973 * @param {Object} config The config object
4976 Roo.bootstrap.Slider = function(config){
4977 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4980 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4982 getAutoCreate : function(){
4986 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4990 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5002 * Ext JS Library 1.1.1
5003 * Copyright(c) 2006-2007, Ext JS, LLC.
5005 * Originally Released Under LGPL - original licence link has changed is not relivant.
5008 * <script type="text/javascript">
5013 * @class Roo.grid.ColumnModel
5014 * @extends Roo.util.Observable
5015 * This is the default implementation of a ColumnModel used by the Grid. It defines
5016 * the columns in the grid.
5019 var colModel = new Roo.grid.ColumnModel([
5020 {header: "Ticker", width: 60, sortable: true, locked: true},
5021 {header: "Company Name", width: 150, sortable: true},
5022 {header: "Market Cap.", width: 100, sortable: true},
5023 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5024 {header: "Employees", width: 100, sortable: true, resizable: false}
5029 * The config options listed for this class are options which may appear in each
5030 * individual column definition.
5031 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5033 * @param {Object} config An Array of column config objects. See this class's
5034 * config objects for details.
5036 Roo.grid.ColumnModel = function(config){
5038 * The config passed into the constructor
5040 this.config = config;
5043 // if no id, create one
5044 // if the column does not have a dataIndex mapping,
5045 // map it to the order it is in the config
5046 for(var i = 0, len = config.length; i < len; i++){
5048 if(typeof c.dataIndex == "undefined"){
5051 if(typeof c.renderer == "string"){
5052 c.renderer = Roo.util.Format[c.renderer];
5054 if(typeof c.id == "undefined"){
5057 if(c.editor && c.editor.xtype){
5058 c.editor = Roo.factory(c.editor, Roo.grid);
5060 if(c.editor && c.editor.isFormField){
5061 c.editor = new Roo.grid.GridEditor(c.editor);
5063 this.lookup[c.id] = c;
5067 * The width of columns which have no width specified (defaults to 100)
5070 this.defaultWidth = 100;
5073 * Default sortable of columns which have no sortable specified (defaults to false)
5076 this.defaultSortable = false;
5080 * @event widthchange
5081 * Fires when the width of a column changes.
5082 * @param {ColumnModel} this
5083 * @param {Number} columnIndex The column index
5084 * @param {Number} newWidth The new width
5086 "widthchange": true,
5088 * @event headerchange
5089 * Fires when the text of a header changes.
5090 * @param {ColumnModel} this
5091 * @param {Number} columnIndex The column index
5092 * @param {Number} newText The new header text
5094 "headerchange": true,
5096 * @event hiddenchange
5097 * Fires when a column is hidden or "unhidden".
5098 * @param {ColumnModel} this
5099 * @param {Number} columnIndex The column index
5100 * @param {Boolean} hidden true if hidden, false otherwise
5102 "hiddenchange": true,
5104 * @event columnmoved
5105 * Fires when a column is moved.
5106 * @param {ColumnModel} this
5107 * @param {Number} oldIndex
5108 * @param {Number} newIndex
5110 "columnmoved" : true,
5112 * @event columlockchange
5113 * Fires when a column's locked state is changed
5114 * @param {ColumnModel} this
5115 * @param {Number} colIndex
5116 * @param {Boolean} locked true if locked
5118 "columnlockchange" : true
5120 Roo.grid.ColumnModel.superclass.constructor.call(this);
5122 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5124 * @cfg {String} header The header text to display in the Grid view.
5127 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5128 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5129 * specified, the column's index is used as an index into the Record's data Array.
5132 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5133 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5136 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5137 * Defaults to the value of the {@link #defaultSortable} property.
5138 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5141 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5144 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5147 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5150 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5153 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5154 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5155 * default renderer uses the raw data value. If an object is returned (bootstrap only)
5156 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5159 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5162 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5165 * @cfg {String} cursor (Optional)
5168 * @cfg {String} tooltip (Optional)
5171 * @cfg {Number} xs (Optional)
5174 * @cfg {Number} sm (Optional)
5177 * @cfg {Number} md (Optional)
5180 * @cfg {Number} lg (Optional)
5183 * Returns the id of the column at the specified index.
5184 * @param {Number} index The column index
5185 * @return {String} the id
5187 getColumnId : function(index){
5188 return this.config[index].id;
5192 * Returns the column for a specified id.
5193 * @param {String} id The column id
5194 * @return {Object} the column
5196 getColumnById : function(id){
5197 return this.lookup[id];
5202 * Returns the column for a specified dataIndex.
5203 * @param {String} dataIndex The column dataIndex
5204 * @return {Object|Boolean} the column or false if not found
5206 getColumnByDataIndex: function(dataIndex){
5207 var index = this.findColumnIndex(dataIndex);
5208 return index > -1 ? this.config[index] : false;
5212 * Returns the index for a specified column id.
5213 * @param {String} id The column id
5214 * @return {Number} the index, or -1 if not found
5216 getIndexById : function(id){
5217 for(var i = 0, len = this.config.length; i < len; i++){
5218 if(this.config[i].id == id){
5226 * Returns the index for a specified column dataIndex.
5227 * @param {String} dataIndex The column dataIndex
5228 * @return {Number} the index, or -1 if not found
5231 findColumnIndex : function(dataIndex){
5232 for(var i = 0, len = this.config.length; i < len; i++){
5233 if(this.config[i].dataIndex == dataIndex){
5241 moveColumn : function(oldIndex, newIndex){
5242 var c = this.config[oldIndex];
5243 this.config.splice(oldIndex, 1);
5244 this.config.splice(newIndex, 0, c);
5245 this.dataMap = null;
5246 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5249 isLocked : function(colIndex){
5250 return this.config[colIndex].locked === true;
5253 setLocked : function(colIndex, value, suppressEvent){
5254 if(this.isLocked(colIndex) == value){
5257 this.config[colIndex].locked = value;
5259 this.fireEvent("columnlockchange", this, colIndex, value);
5263 getTotalLockedWidth : function(){
5265 for(var i = 0; i < this.config.length; i++){
5266 if(this.isLocked(i) && !this.isHidden(i)){
5267 this.totalWidth += this.getColumnWidth(i);
5273 getLockedCount : function(){
5274 for(var i = 0, len = this.config.length; i < len; i++){
5275 if(!this.isLocked(i)){
5280 return this.config.length;
5284 * Returns the number of columns.
5287 getColumnCount : function(visibleOnly){
5288 if(visibleOnly === true){
5290 for(var i = 0, len = this.config.length; i < len; i++){
5291 if(!this.isHidden(i)){
5297 return this.config.length;
5301 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5302 * @param {Function} fn
5303 * @param {Object} scope (optional)
5304 * @return {Array} result
5306 getColumnsBy : function(fn, scope){
5308 for(var i = 0, len = this.config.length; i < len; i++){
5309 var c = this.config[i];
5310 if(fn.call(scope||this, c, i) === true){
5318 * Returns true if the specified column is sortable.
5319 * @param {Number} col The column index
5322 isSortable : function(col){
5323 if(typeof this.config[col].sortable == "undefined"){
5324 return this.defaultSortable;
5326 return this.config[col].sortable;
5330 * Returns the rendering (formatting) function defined for the column.
5331 * @param {Number} col The column index.
5332 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5334 getRenderer : function(col){
5335 if(!this.config[col].renderer){
5336 return Roo.grid.ColumnModel.defaultRenderer;
5338 return this.config[col].renderer;
5342 * Sets the rendering (formatting) function for a column.
5343 * @param {Number} col The column index
5344 * @param {Function} fn The function to use to process the cell's raw data
5345 * to return HTML markup for the grid view. The render function is called with
5346 * the following parameters:<ul>
5347 * <li>Data value.</li>
5348 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5349 * <li>css A CSS style string to apply to the table cell.</li>
5350 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5351 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5352 * <li>Row index</li>
5353 * <li>Column index</li>
5354 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5356 setRenderer : function(col, fn){
5357 this.config[col].renderer = fn;
5361 * Returns the width for the specified column.
5362 * @param {Number} col The column index
5365 getColumnWidth : function(col){
5366 return this.config[col].width * 1 || this.defaultWidth;
5370 * Sets the width for a column.
5371 * @param {Number} col The column index
5372 * @param {Number} width The new width
5374 setColumnWidth : function(col, width, suppressEvent){
5375 this.config[col].width = width;
5376 this.totalWidth = null;
5378 this.fireEvent("widthchange", this, col, width);
5383 * Returns the total width of all columns.
5384 * @param {Boolean} includeHidden True to include hidden column widths
5387 getTotalWidth : function(includeHidden){
5388 if(!this.totalWidth){
5389 this.totalWidth = 0;
5390 for(var i = 0, len = this.config.length; i < len; i++){
5391 if(includeHidden || !this.isHidden(i)){
5392 this.totalWidth += this.getColumnWidth(i);
5396 return this.totalWidth;
5400 * Returns the header for the specified column.
5401 * @param {Number} col The column index
5404 getColumnHeader : function(col){
5405 return this.config[col].header;
5409 * Sets the header for a column.
5410 * @param {Number} col The column index
5411 * @param {String} header The new header
5413 setColumnHeader : function(col, header){
5414 this.config[col].header = header;
5415 this.fireEvent("headerchange", this, col, header);
5419 * Returns the tooltip for the specified column.
5420 * @param {Number} col The column index
5423 getColumnTooltip : function(col){
5424 return this.config[col].tooltip;
5427 * Sets the tooltip for a column.
5428 * @param {Number} col The column index
5429 * @param {String} tooltip The new tooltip
5431 setColumnTooltip : function(col, tooltip){
5432 this.config[col].tooltip = tooltip;
5436 * Returns the dataIndex for the specified column.
5437 * @param {Number} col The column index
5440 getDataIndex : function(col){
5441 return this.config[col].dataIndex;
5445 * Sets the dataIndex for a column.
5446 * @param {Number} col The column index
5447 * @param {Number} dataIndex The new dataIndex
5449 setDataIndex : function(col, dataIndex){
5450 this.config[col].dataIndex = dataIndex;
5456 * Returns true if the cell is editable.
5457 * @param {Number} colIndex The column index
5458 * @param {Number} rowIndex The row index - this is nto actually used..?
5461 isCellEditable : function(colIndex, rowIndex){
5462 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5466 * Returns the editor defined for the cell/column.
5467 * return false or null to disable editing.
5468 * @param {Number} colIndex The column index
5469 * @param {Number} rowIndex The row index
5472 getCellEditor : function(colIndex, rowIndex){
5473 return this.config[colIndex].editor;
5477 * Sets if a column is editable.
5478 * @param {Number} col The column index
5479 * @param {Boolean} editable True if the column is editable
5481 setEditable : function(col, editable){
5482 this.config[col].editable = editable;
5487 * Returns true if the column is hidden.
5488 * @param {Number} colIndex The column index
5491 isHidden : function(colIndex){
5492 return this.config[colIndex].hidden;
5497 * Returns true if the column width cannot be changed
5499 isFixed : function(colIndex){
5500 return this.config[colIndex].fixed;
5504 * Returns true if the column can be resized
5507 isResizable : function(colIndex){
5508 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5511 * Sets if a column is hidden.
5512 * @param {Number} colIndex The column index
5513 * @param {Boolean} hidden True if the column is hidden
5515 setHidden : function(colIndex, hidden){
5516 this.config[colIndex].hidden = hidden;
5517 this.totalWidth = null;
5518 this.fireEvent("hiddenchange", this, colIndex, hidden);
5522 * Sets the editor for a column.
5523 * @param {Number} col The column index
5524 * @param {Object} editor The editor object
5526 setEditor : function(col, editor){
5527 this.config[col].editor = editor;
5531 Roo.grid.ColumnModel.defaultRenderer = function(value){
5532 if(typeof value == "string" && value.length < 1){
5538 // Alias for backwards compatibility
5539 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5542 * Ext JS Library 1.1.1
5543 * Copyright(c) 2006-2007, Ext JS, LLC.
5545 * Originally Released Under LGPL - original licence link has changed is not relivant.
5548 * <script type="text/javascript">
5552 * @class Roo.LoadMask
5553 * A simple utility class for generically masking elements while loading data. If the element being masked has
5554 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5555 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5556 * element's UpdateManager load indicator and will be destroyed after the initial load.
5558 * Create a new LoadMask
5559 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5560 * @param {Object} config The config object
5562 Roo.LoadMask = function(el, config){
5563 this.el = Roo.get(el);
5564 Roo.apply(this, config);
5566 this.store.on('beforeload', this.onBeforeLoad, this);
5567 this.store.on('load', this.onLoad, this);
5568 this.store.on('loadexception', this.onLoadException, this);
5569 this.removeMask = false;
5571 var um = this.el.getUpdateManager();
5572 um.showLoadIndicator = false; // disable the default indicator
5573 um.on('beforeupdate', this.onBeforeLoad, this);
5574 um.on('update', this.onLoad, this);
5575 um.on('failure', this.onLoad, this);
5576 this.removeMask = true;
5580 Roo.LoadMask.prototype = {
5582 * @cfg {Boolean} removeMask
5583 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5584 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5588 * The text to display in a centered loading message box (defaults to 'Loading...')
5592 * @cfg {String} msgCls
5593 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5595 msgCls : 'x-mask-loading',
5598 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5604 * Disables the mask to prevent it from being displayed
5606 disable : function(){
5607 this.disabled = true;
5611 * Enables the mask so that it can be displayed
5613 enable : function(){
5614 this.disabled = false;
5617 onLoadException : function()
5621 if (typeof(arguments[3]) != 'undefined') {
5622 Roo.MessageBox.alert("Error loading",arguments[3]);
5626 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5627 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5636 this.el.unmask(this.removeMask);
5641 this.el.unmask(this.removeMask);
5645 onBeforeLoad : function(){
5647 this.el.mask(this.msg, this.msgCls);
5652 destroy : function(){
5654 this.store.un('beforeload', this.onBeforeLoad, this);
5655 this.store.un('load', this.onLoad, this);
5656 this.store.un('loadexception', this.onLoadException, this);
5658 var um = this.el.getUpdateManager();
5659 um.un('beforeupdate', this.onBeforeLoad, this);
5660 um.un('update', this.onLoad, this);
5661 um.un('failure', this.onLoad, this);
5672 * @class Roo.bootstrap.Table
5673 * @extends Roo.bootstrap.Component
5674 * Bootstrap Table class
5675 * @cfg {String} cls table class
5676 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5677 * @cfg {String} bgcolor Specifies the background color for a table
5678 * @cfg {Number} border Specifies whether the table cells should have borders or not
5679 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5680 * @cfg {Number} cellspacing Specifies the space between cells
5681 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5682 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5683 * @cfg {String} sortable Specifies that the table should be sortable
5684 * @cfg {String} summary Specifies a summary of the content of a table
5685 * @cfg {Number} width Specifies the width of a table
5686 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5688 * @cfg {boolean} striped Should the rows be alternative striped
5689 * @cfg {boolean} bordered Add borders to the table
5690 * @cfg {boolean} hover Add hover highlighting
5691 * @cfg {boolean} condensed Format condensed
5692 * @cfg {boolean} responsive Format condensed
5693 * @cfg {Boolean} loadMask (true|false) default false
5694 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5695 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5696 * @cfg {Boolean} rowSelection (true|false) default false
5697 * @cfg {Boolean} cellSelection (true|false) default false
5698 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5699 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5703 * Create a new Table
5704 * @param {Object} config The config object
5707 Roo.bootstrap.Table = function(config){
5708 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5713 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5714 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5715 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5716 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5718 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5720 this.sm.grid = this;
5721 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5722 this.sm = this.selModel;
5723 this.sm.xmodule = this.xmodule || false;
5726 if (this.cm && typeof(this.cm.config) == 'undefined') {
5727 this.colModel = new Roo.grid.ColumnModel(this.cm);
5728 this.cm = this.colModel;
5729 this.cm.xmodule = this.xmodule || false;
5732 this.store= Roo.factory(this.store, Roo.data);
5733 this.ds = this.store;
5734 this.ds.xmodule = this.xmodule || false;
5737 if (this.footer && this.store) {
5738 this.footer.dataSource = this.ds;
5739 this.footer = Roo.factory(this.footer);
5746 * Fires when a cell is clicked
5747 * @param {Roo.bootstrap.Table} this
5748 * @param {Roo.Element} el
5749 * @param {Number} rowIndex
5750 * @param {Number} columnIndex
5751 * @param {Roo.EventObject} e
5755 * @event celldblclick
5756 * Fires when a cell is double clicked
5757 * @param {Roo.bootstrap.Table} this
5758 * @param {Roo.Element} el
5759 * @param {Number} rowIndex
5760 * @param {Number} columnIndex
5761 * @param {Roo.EventObject} e
5763 "celldblclick" : true,
5766 * Fires when a row is clicked
5767 * @param {Roo.bootstrap.Table} this
5768 * @param {Roo.Element} el
5769 * @param {Number} rowIndex
5770 * @param {Roo.EventObject} e
5774 * @event rowdblclick
5775 * Fires when a row is double clicked
5776 * @param {Roo.bootstrap.Table} this
5777 * @param {Roo.Element} el
5778 * @param {Number} rowIndex
5779 * @param {Roo.EventObject} e
5781 "rowdblclick" : true,
5784 * Fires when a mouseover occur
5785 * @param {Roo.bootstrap.Table} this
5786 * @param {Roo.Element} el
5787 * @param {Number} rowIndex
5788 * @param {Number} columnIndex
5789 * @param {Roo.EventObject} e
5794 * Fires when a mouseout occur
5795 * @param {Roo.bootstrap.Table} this
5796 * @param {Roo.Element} el
5797 * @param {Number} rowIndex
5798 * @param {Number} columnIndex
5799 * @param {Roo.EventObject} e
5804 * Fires when a row is rendered, so you can change add a style to it.
5805 * @param {Roo.bootstrap.Table} this
5806 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5810 * @event rowsrendered
5811 * Fires when all the rows have been rendered
5812 * @param {Roo.bootstrap.Table} this
5814 'rowsrendered' : true,
5816 * @event contextmenu
5817 * The raw contextmenu event for the entire grid.
5818 * @param {Roo.EventObject} e
5820 "contextmenu" : true,
5822 * @event rowcontextmenu
5823 * Fires when a row is right clicked
5824 * @param {Roo.bootstrap.Table} this
5825 * @param {Number} rowIndex
5826 * @param {Roo.EventObject} e
5828 "rowcontextmenu" : true,
5830 * @event cellcontextmenu
5831 * Fires when a cell is right clicked
5832 * @param {Roo.bootstrap.Table} this
5833 * @param {Number} rowIndex
5834 * @param {Number} cellIndex
5835 * @param {Roo.EventObject} e
5837 "cellcontextmenu" : true,
5839 * @event headercontextmenu
5840 * Fires when a header is right clicked
5841 * @param {Roo.bootstrap.Table} this
5842 * @param {Number} columnIndex
5843 * @param {Roo.EventObject} e
5845 "headercontextmenu" : true
5849 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5875 rowSelection : false,
5876 cellSelection : false,
5879 // Roo.Element - the tbody
5881 // Roo.Element - thead element
5884 container: false, // used by gridpanel...
5886 getAutoCreate : function()
5888 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5895 if (this.scrollBody) {
5896 cfg.cls += ' table-body-fixed';
5899 cfg.cls += ' table-striped';
5903 cfg.cls += ' table-hover';
5905 if (this.bordered) {
5906 cfg.cls += ' table-bordered';
5908 if (this.condensed) {
5909 cfg.cls += ' table-condensed';
5911 if (this.responsive) {
5912 cfg.cls += ' table-responsive';
5916 cfg.cls+= ' ' +this.cls;
5919 // this lot should be simplifed...
5922 cfg.align=this.align;
5925 cfg.bgcolor=this.bgcolor;
5928 cfg.border=this.border;
5930 if (this.cellpadding) {
5931 cfg.cellpadding=this.cellpadding;
5933 if (this.cellspacing) {
5934 cfg.cellspacing=this.cellspacing;
5937 cfg.frame=this.frame;
5940 cfg.rules=this.rules;
5942 if (this.sortable) {
5943 cfg.sortable=this.sortable;
5946 cfg.summary=this.summary;
5949 cfg.width=this.width;
5952 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5955 if(this.store || this.cm){
5956 if(this.headerShow){
5957 cfg.cn.push(this.renderHeader());
5960 cfg.cn.push(this.renderBody());
5962 if(this.footerShow){
5963 cfg.cn.push(this.renderFooter());
5965 // where does this come from?
5966 //cfg.cls+= ' TableGrid';
5969 return { cn : [ cfg ] };
5972 initEvents : function()
5974 if(!this.store || !this.cm){
5977 if (this.selModel) {
5978 this.selModel.initEvents();
5982 //Roo.log('initEvents with ds!!!!');
5984 this.mainBody = this.el.select('tbody', true).first();
5985 this.mainHead = this.el.select('thead', true).first();
5992 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5993 e.on('click', _this.sort, _this);
5996 this.el.on("click", this.onClick, this);
5997 this.el.on("dblclick", this.onDblClick, this);
5999 // why is this done????? = it breaks dialogs??
6000 //this.parent().el.setStyle('position', 'relative');
6004 this.footer.parentId = this.id;
6005 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6008 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6010 this.store.on('load', this.onLoad, this);
6011 this.store.on('beforeload', this.onBeforeLoad, this);
6012 this.store.on('update', this.onUpdate, this);
6013 this.store.on('add', this.onAdd, this);
6014 this.store.on("clear", this.clear, this);
6016 this.el.on("contextmenu", this.onContextMenu, this);
6018 this.mainBody.on('scroll', this.onBodyScroll, this);
6023 onContextMenu : function(e, t)
6025 this.processEvent("contextmenu", e);
6028 processEvent : function(name, e)
6030 if (name != 'touchstart' ) {
6031 this.fireEvent(name, e);
6034 var t = e.getTarget();
6036 var cell = Roo.get(t);
6042 if(cell.findParent('tfoot', false, true)){
6046 if(cell.findParent('thead', false, true)){
6048 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6049 cell = Roo.get(t).findParent('th', false, true);
6051 Roo.log("failed to find th in thead?");
6052 Roo.log(e.getTarget());
6057 var cellIndex = cell.dom.cellIndex;
6059 var ename = name == 'touchstart' ? 'click' : name;
6060 this.fireEvent("header" + ename, this, cellIndex, e);
6065 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6066 cell = Roo.get(t).findParent('td', false, true);
6068 Roo.log("failed to find th in tbody?");
6069 Roo.log(e.getTarget());
6074 var row = cell.findParent('tr', false, true);
6075 var cellIndex = cell.dom.cellIndex;
6076 var rowIndex = row.dom.rowIndex - 1;
6080 this.fireEvent("row" + name, this, rowIndex, e);
6084 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6090 onMouseover : function(e, el)
6092 var cell = Roo.get(el);
6098 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6099 cell = cell.findParent('td', false, true);
6102 var row = cell.findParent('tr', false, true);
6103 var cellIndex = cell.dom.cellIndex;
6104 var rowIndex = row.dom.rowIndex - 1; // start from 0
6106 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6110 onMouseout : function(e, el)
6112 var cell = Roo.get(el);
6118 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6119 cell = cell.findParent('td', false, true);
6122 var row = cell.findParent('tr', false, true);
6123 var cellIndex = cell.dom.cellIndex;
6124 var rowIndex = row.dom.rowIndex - 1; // start from 0
6126 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6130 onClick : function(e, el)
6132 var cell = Roo.get(el);
6134 if(!cell || (!this.cellSelection && !this.rowSelection)){
6138 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6139 cell = cell.findParent('td', false, true);
6142 if(!cell || typeof(cell) == 'undefined'){
6146 var row = cell.findParent('tr', false, true);
6148 if(!row || typeof(row) == 'undefined'){
6152 var cellIndex = cell.dom.cellIndex;
6153 var rowIndex = this.getRowIndex(row);
6155 // why??? - should these not be based on SelectionModel?
6156 if(this.cellSelection){
6157 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6160 if(this.rowSelection){
6161 this.fireEvent('rowclick', this, row, rowIndex, e);
6167 onDblClick : function(e,el)
6169 var cell = Roo.get(el);
6171 if(!cell || (!this.cellSelection && !this.rowSelection)){
6175 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6176 cell = cell.findParent('td', false, true);
6179 if(!cell || typeof(cell) == 'undefined'){
6183 var row = cell.findParent('tr', false, true);
6185 if(!row || typeof(row) == 'undefined'){
6189 var cellIndex = cell.dom.cellIndex;
6190 var rowIndex = this.getRowIndex(row);
6192 if(this.cellSelection){
6193 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6196 if(this.rowSelection){
6197 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6201 sort : function(e,el)
6203 var col = Roo.get(el);
6205 if(!col.hasClass('sortable')){
6209 var sort = col.attr('sort');
6212 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6216 this.store.sortInfo = {field : sort, direction : dir};
6219 Roo.log("calling footer first");
6220 this.footer.onClick('first');
6223 this.store.load({ params : { start : 0 } });
6227 renderHeader : function()
6235 this.totalWidth = 0;
6237 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6239 var config = cm.config[i];
6244 html: cm.getColumnHeader(i)
6249 if(typeof(config.sortable) != 'undefined' && config.sortable){
6251 c.html = '<i class="glyphicon"></i>' + c.html;
6254 if(typeof(config.lgHeader) != 'undefined'){
6255 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6258 if(typeof(config.mdHeader) != 'undefined'){
6259 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6262 if(typeof(config.smHeader) != 'undefined'){
6263 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6266 if(typeof(config.xsHeader) != 'undefined'){
6267 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6274 if(typeof(config.tooltip) != 'undefined'){
6275 c.tooltip = config.tooltip;
6278 if(typeof(config.colspan) != 'undefined'){
6279 c.colspan = config.colspan;
6282 if(typeof(config.hidden) != 'undefined' && config.hidden){
6283 c.style += ' display:none;';
6286 if(typeof(config.dataIndex) != 'undefined'){
6287 c.sort = config.dataIndex;
6292 if(typeof(config.align) != 'undefined' && config.align.length){
6293 c.style += ' text-align:' + config.align + ';';
6296 if(typeof(config.width) != 'undefined'){
6297 c.style += ' width:' + config.width + 'px;';
6298 this.totalWidth += config.width;
6300 this.totalWidth += 100; // assume minimum of 100 per column?
6303 if(typeof(config.cls) != 'undefined'){
6304 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6307 ['xs','sm','md','lg'].map(function(size){
6309 if(typeof(config[size]) == 'undefined'){
6313 if (!config[size]) { // 0 = hidden
6314 c.cls += ' hidden-' + size;
6318 c.cls += ' col-' + size + '-' + config[size];
6328 renderBody : function()
6338 colspan : this.cm.getColumnCount()
6348 renderFooter : function()
6358 colspan : this.cm.getColumnCount()
6372 // Roo.log('ds onload');
6377 var ds = this.store;
6379 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6380 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6381 if (_this.store.sortInfo) {
6383 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6384 e.select('i', true).addClass(['glyphicon-arrow-up']);
6387 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6388 e.select('i', true).addClass(['glyphicon-arrow-down']);
6393 var tbody = this.mainBody;
6395 if(ds.getCount() > 0){
6396 ds.data.each(function(d,rowIndex){
6397 var row = this.renderRow(cm, ds, rowIndex);
6399 tbody.createChild(row);
6403 if(row.cellObjects.length){
6404 Roo.each(row.cellObjects, function(r){
6405 _this.renderCellObject(r);
6412 Roo.each(this.el.select('tbody td', true).elements, function(e){
6413 e.on('mouseover', _this.onMouseover, _this);
6416 Roo.each(this.el.select('tbody td', true).elements, function(e){
6417 e.on('mouseout', _this.onMouseout, _this);
6419 this.fireEvent('rowsrendered', this);
6420 //if(this.loadMask){
6421 // this.maskEl.hide();
6428 onUpdate : function(ds,record)
6430 this.refreshRow(record);
6434 onRemove : function(ds, record, index, isUpdate){
6435 if(isUpdate !== true){
6436 this.fireEvent("beforerowremoved", this, index, record);
6438 var bt = this.mainBody.dom;
6440 var rows = this.el.select('tbody > tr', true).elements;
6442 if(typeof(rows[index]) != 'undefined'){
6443 bt.removeChild(rows[index].dom);
6446 // if(bt.rows[index]){
6447 // bt.removeChild(bt.rows[index]);
6450 if(isUpdate !== true){
6451 //this.stripeRows(index);
6452 //this.syncRowHeights(index, index);
6454 this.fireEvent("rowremoved", this, index, record);
6458 onAdd : function(ds, records, rowIndex)
6460 //Roo.log('on Add called');
6461 // - note this does not handle multiple adding very well..
6462 var bt = this.mainBody.dom;
6463 for (var i =0 ; i < records.length;i++) {
6464 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6465 //Roo.log(records[i]);
6466 //Roo.log(this.store.getAt(rowIndex+i));
6467 this.insertRow(this.store, rowIndex + i, false);
6474 refreshRow : function(record){
6475 var ds = this.store, index;
6476 if(typeof record == 'number'){
6478 record = ds.getAt(index);
6480 index = ds.indexOf(record);
6482 this.insertRow(ds, index, true);
6484 this.onRemove(ds, record, index+1, true);
6486 //this.syncRowHeights(index, index);
6488 this.fireEvent("rowupdated", this, index, record);
6491 insertRow : function(dm, rowIndex, isUpdate){
6494 this.fireEvent("beforerowsinserted", this, rowIndex);
6496 //var s = this.getScrollState();
6497 var row = this.renderRow(this.cm, this.store, rowIndex);
6498 // insert before rowIndex..
6499 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6503 if(row.cellObjects.length){
6504 Roo.each(row.cellObjects, function(r){
6505 _this.renderCellObject(r);
6510 this.fireEvent("rowsinserted", this, rowIndex);
6511 //this.syncRowHeights(firstRow, lastRow);
6512 //this.stripeRows(firstRow);
6519 getRowDom : function(rowIndex)
6521 var rows = this.el.select('tbody > tr', true).elements;
6523 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6526 // returns the object tree for a tr..
6529 renderRow : function(cm, ds, rowIndex)
6532 var d = ds.getAt(rowIndex);
6539 var cellObjects = [];
6541 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6542 var config = cm.config[i];
6544 var renderer = cm.getRenderer(i);
6548 if(typeof(renderer) !== 'undefined'){
6549 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6551 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6552 // and are rendered into the cells after the row is rendered - using the id for the element.
6554 if(typeof(value) === 'object'){
6564 rowIndex : rowIndex,
6569 this.fireEvent('rowclass', this, rowcfg);
6573 cls : rowcfg.rowClass,
6575 html: (typeof(value) === 'object') ? '' : value
6582 if(typeof(config.colspan) != 'undefined'){
6583 td.colspan = config.colspan;
6586 if(typeof(config.hidden) != 'undefined' && config.hidden){
6587 td.style += ' display:none;';
6590 if(typeof(config.align) != 'undefined' && config.align.length){
6591 td.style += ' text-align:' + config.align + ';';
6594 if(typeof(config.width) != 'undefined'){
6595 td.style += ' width:' + config.width + 'px;';
6598 if(typeof(config.cursor) != 'undefined'){
6599 td.style += ' cursor:' + config.cursor + ';';
6602 if(typeof(config.cls) != 'undefined'){
6603 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6606 ['xs','sm','md','lg'].map(function(size){
6608 if(typeof(config[size]) == 'undefined'){
6612 if (!config[size]) { // 0 = hidden
6613 td.cls += ' hidden-' + size;
6617 td.cls += ' col-' + size + '-' + config[size];
6625 row.cellObjects = cellObjects;
6633 onBeforeLoad : function()
6635 //Roo.log('ds onBeforeLoad');
6639 //if(this.loadMask){
6640 // this.maskEl.show();
6648 this.el.select('tbody', true).first().dom.innerHTML = '';
6651 * Show or hide a row.
6652 * @param {Number} rowIndex to show or hide
6653 * @param {Boolean} state hide
6655 setRowVisibility : function(rowIndex, state)
6657 var bt = this.mainBody.dom;
6659 var rows = this.el.select('tbody > tr', true).elements;
6661 if(typeof(rows[rowIndex]) == 'undefined'){
6664 rows[rowIndex].dom.style.display = state ? '' : 'none';
6668 getSelectionModel : function(){
6670 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6672 return this.selModel;
6675 * Render the Roo.bootstrap object from renderder
6677 renderCellObject : function(r)
6681 var t = r.cfg.render(r.container);
6684 Roo.each(r.cfg.cn, function(c){
6686 container: t.getChildContainer(),
6689 _this.renderCellObject(child);
6694 getRowIndex : function(row)
6698 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6709 * Returns the grid's underlying element = used by panel.Grid
6710 * @return {Element} The element
6712 getGridEl : function(){
6716 * Forces a resize - used by panel.Grid
6717 * @return {Element} The element
6719 autoSize : function()
6721 //var ctr = Roo.get(this.container.dom.parentElement);
6722 var ctr = Roo.get(this.el.dom);
6724 var thd = this.getGridEl().select('thead',true).first();
6725 var tbd = this.getGridEl().select('tbody', true).first();
6728 var cw = ctr.getWidth();
6732 tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6733 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6736 cw = Math.max(cw, this.totalWidth);
6737 this.getGridEl().select('tr',true).setWidth(cw);
6738 // resize 'expandable coloumn?
6740 return; // we doe not have a view in this design..
6743 onBodyScroll: function()
6746 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6747 this.mainHead.setStyle({
6748 'position' : 'relative',
6749 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6766 * @class Roo.bootstrap.TableCell
6767 * @extends Roo.bootstrap.Component
6768 * Bootstrap TableCell class
6769 * @cfg {String} html cell contain text
6770 * @cfg {String} cls cell class
6771 * @cfg {String} tag cell tag (td|th) default td
6772 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6773 * @cfg {String} align Aligns the content in a cell
6774 * @cfg {String} axis Categorizes cells
6775 * @cfg {String} bgcolor Specifies the background color of a cell
6776 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6777 * @cfg {Number} colspan Specifies the number of columns a cell should span
6778 * @cfg {String} headers Specifies one or more header cells a cell is related to
6779 * @cfg {Number} height Sets the height of a cell
6780 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6781 * @cfg {Number} rowspan Sets the number of rows a cell should span
6782 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6783 * @cfg {String} valign Vertical aligns the content in a cell
6784 * @cfg {Number} width Specifies the width of a cell
6787 * Create a new TableCell
6788 * @param {Object} config The config object
6791 Roo.bootstrap.TableCell = function(config){
6792 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6795 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6815 getAutoCreate : function(){
6816 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6836 cfg.align=this.align
6842 cfg.bgcolor=this.bgcolor
6845 cfg.charoff=this.charoff
6848 cfg.colspan=this.colspan
6851 cfg.headers=this.headers
6854 cfg.height=this.height
6857 cfg.nowrap=this.nowrap
6860 cfg.rowspan=this.rowspan
6863 cfg.scope=this.scope
6866 cfg.valign=this.valign
6869 cfg.width=this.width
6888 * @class Roo.bootstrap.TableRow
6889 * @extends Roo.bootstrap.Component
6890 * Bootstrap TableRow class
6891 * @cfg {String} cls row class
6892 * @cfg {String} align Aligns the content in a table row
6893 * @cfg {String} bgcolor Specifies a background color for a table row
6894 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6895 * @cfg {String} valign Vertical aligns the content in a table row
6898 * Create a new TableRow
6899 * @param {Object} config The config object
6902 Roo.bootstrap.TableRow = function(config){
6903 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6906 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6914 getAutoCreate : function(){
6915 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6925 cfg.align = this.align;
6928 cfg.bgcolor = this.bgcolor;
6931 cfg.charoff = this.charoff;
6934 cfg.valign = this.valign;
6952 * @class Roo.bootstrap.TableBody
6953 * @extends Roo.bootstrap.Component
6954 * Bootstrap TableBody class
6955 * @cfg {String} cls element class
6956 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6957 * @cfg {String} align Aligns the content inside the element
6958 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6959 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6962 * Create a new TableBody
6963 * @param {Object} config The config object
6966 Roo.bootstrap.TableBody = function(config){
6967 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6970 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6978 getAutoCreate : function(){
6979 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6993 cfg.align = this.align;
6996 cfg.charoff = this.charoff;
6999 cfg.valign = this.valign;
7006 // initEvents : function()
7013 // this.store = Roo.factory(this.store, Roo.data);
7014 // this.store.on('load', this.onLoad, this);
7016 // this.store.load();
7020 // onLoad: function ()
7022 // this.fireEvent('load', this);
7032 * Ext JS Library 1.1.1
7033 * Copyright(c) 2006-2007, Ext JS, LLC.
7035 * Originally Released Under LGPL - original licence link has changed is not relivant.
7038 * <script type="text/javascript">
7041 // as we use this in bootstrap.
7042 Roo.namespace('Roo.form');
7044 * @class Roo.form.Action
7045 * Internal Class used to handle form actions
7047 * @param {Roo.form.BasicForm} el The form element or its id
7048 * @param {Object} config Configuration options
7053 // define the action interface
7054 Roo.form.Action = function(form, options){
7056 this.options = options || {};
7059 * Client Validation Failed
7062 Roo.form.Action.CLIENT_INVALID = 'client';
7064 * Server Validation Failed
7067 Roo.form.Action.SERVER_INVALID = 'server';
7069 * Connect to Server Failed
7072 Roo.form.Action.CONNECT_FAILURE = 'connect';
7074 * Reading Data from Server Failed
7077 Roo.form.Action.LOAD_FAILURE = 'load';
7079 Roo.form.Action.prototype = {
7081 failureType : undefined,
7082 response : undefined,
7086 run : function(options){
7091 success : function(response){
7096 handleResponse : function(response){
7100 // default connection failure
7101 failure : function(response){
7103 this.response = response;
7104 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7105 this.form.afterAction(this, false);
7108 processResponse : function(response){
7109 this.response = response;
7110 if(!response.responseText){
7113 this.result = this.handleResponse(response);
7117 // utility functions used internally
7118 getUrl : function(appendParams){
7119 var url = this.options.url || this.form.url || this.form.el.dom.action;
7121 var p = this.getParams();
7123 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7129 getMethod : function(){
7130 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7133 getParams : function(){
7134 var bp = this.form.baseParams;
7135 var p = this.options.params;
7137 if(typeof p == "object"){
7138 p = Roo.urlEncode(Roo.applyIf(p, bp));
7139 }else if(typeof p == 'string' && bp){
7140 p += '&' + Roo.urlEncode(bp);
7143 p = Roo.urlEncode(bp);
7148 createCallback : function(){
7150 success: this.success,
7151 failure: this.failure,
7153 timeout: (this.form.timeout*1000),
7154 upload: this.form.fileUpload ? this.success : undefined
7159 Roo.form.Action.Submit = function(form, options){
7160 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7163 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7166 haveProgress : false,
7167 uploadComplete : false,
7169 // uploadProgress indicator.
7170 uploadProgress : function()
7172 if (!this.form.progressUrl) {
7176 if (!this.haveProgress) {
7177 Roo.MessageBox.progress("Uploading", "Uploading");
7179 if (this.uploadComplete) {
7180 Roo.MessageBox.hide();
7184 this.haveProgress = true;
7186 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7188 var c = new Roo.data.Connection();
7190 url : this.form.progressUrl,
7195 success : function(req){
7196 //console.log(data);
7200 rdata = Roo.decode(req.responseText)
7202 Roo.log("Invalid data from server..");
7206 if (!rdata || !rdata.success) {
7208 Roo.MessageBox.alert(Roo.encode(rdata));
7211 var data = rdata.data;
7213 if (this.uploadComplete) {
7214 Roo.MessageBox.hide();
7219 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7220 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7223 this.uploadProgress.defer(2000,this);
7226 failure: function(data) {
7227 Roo.log('progress url failed ');
7238 // run get Values on the form, so it syncs any secondary forms.
7239 this.form.getValues();
7241 var o = this.options;
7242 var method = this.getMethod();
7243 var isPost = method == 'POST';
7244 if(o.clientValidation === false || this.form.isValid()){
7246 if (this.form.progressUrl) {
7247 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7248 (new Date() * 1) + '' + Math.random());
7253 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7254 form:this.form.el.dom,
7255 url:this.getUrl(!isPost),
7257 params:isPost ? this.getParams() : null,
7258 isUpload: this.form.fileUpload
7261 this.uploadProgress();
7263 }else if (o.clientValidation !== false){ // client validation failed
7264 this.failureType = Roo.form.Action.CLIENT_INVALID;
7265 this.form.afterAction(this, false);
7269 success : function(response)
7271 this.uploadComplete= true;
7272 if (this.haveProgress) {
7273 Roo.MessageBox.hide();
7277 var result = this.processResponse(response);
7278 if(result === true || result.success){
7279 this.form.afterAction(this, true);
7283 this.form.markInvalid(result.errors);
7284 this.failureType = Roo.form.Action.SERVER_INVALID;
7286 this.form.afterAction(this, false);
7288 failure : function(response)
7290 this.uploadComplete= true;
7291 if (this.haveProgress) {
7292 Roo.MessageBox.hide();
7295 this.response = response;
7296 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7297 this.form.afterAction(this, false);
7300 handleResponse : function(response){
7301 if(this.form.errorReader){
7302 var rs = this.form.errorReader.read(response);
7305 for(var i = 0, len = rs.records.length; i < len; i++) {
7306 var r = rs.records[i];
7310 if(errors.length < 1){
7314 success : rs.success,
7320 ret = Roo.decode(response.responseText);
7324 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7334 Roo.form.Action.Load = function(form, options){
7335 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7336 this.reader = this.form.reader;
7339 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7344 Roo.Ajax.request(Roo.apply(
7345 this.createCallback(), {
7346 method:this.getMethod(),
7347 url:this.getUrl(false),
7348 params:this.getParams()
7352 success : function(response){
7354 var result = this.processResponse(response);
7355 if(result === true || !result.success || !result.data){
7356 this.failureType = Roo.form.Action.LOAD_FAILURE;
7357 this.form.afterAction(this, false);
7360 this.form.clearInvalid();
7361 this.form.setValues(result.data);
7362 this.form.afterAction(this, true);
7365 handleResponse : function(response){
7366 if(this.form.reader){
7367 var rs = this.form.reader.read(response);
7368 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7370 success : rs.success,
7374 return Roo.decode(response.responseText);
7378 Roo.form.Action.ACTION_TYPES = {
7379 'load' : Roo.form.Action.Load,
7380 'submit' : Roo.form.Action.Submit
7389 * @class Roo.bootstrap.Form
7390 * @extends Roo.bootstrap.Component
7391 * Bootstrap Form class
7392 * @cfg {String} method GET | POST (default POST)
7393 * @cfg {String} labelAlign top | left (default top)
7394 * @cfg {String} align left | right - for navbars
7395 * @cfg {Boolean} loadMask load mask when submit (default true)
7400 * @param {Object} config The config object
7404 Roo.bootstrap.Form = function(config){
7405 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7408 * @event clientvalidation
7409 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7410 * @param {Form} this
7411 * @param {Boolean} valid true if the form has passed client-side validation
7413 clientvalidation: true,
7415 * @event beforeaction
7416 * Fires before any action is performed. Return false to cancel the action.
7417 * @param {Form} this
7418 * @param {Action} action The action to be performed
7422 * @event actionfailed
7423 * Fires when an action fails.
7424 * @param {Form} this
7425 * @param {Action} action The action that failed
7427 actionfailed : true,
7429 * @event actioncomplete
7430 * Fires when an action is completed.
7431 * @param {Form} this
7432 * @param {Action} action The action that completed
7434 actioncomplete : true
7439 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7442 * @cfg {String} method
7443 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7448 * The URL to use for form actions if one isn't supplied in the action options.
7451 * @cfg {Boolean} fileUpload
7452 * Set to true if this form is a file upload.
7456 * @cfg {Object} baseParams
7457 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7461 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7465 * @cfg {Sting} align (left|right) for navbar forms
7470 activeAction : null,
7473 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7474 * element by passing it or its id or mask the form itself by passing in true.
7477 waitMsgTarget : false,
7481 getAutoCreate : function(){
7485 method : this.method || 'POST',
7486 id : this.id || Roo.id(),
7489 if (this.parent().xtype.match(/^Nav/)) {
7490 cfg.cls = 'navbar-form navbar-' + this.align;
7494 if (this.labelAlign == 'left' ) {
7495 cfg.cls += ' form-horizontal';
7501 initEvents : function()
7503 this.el.on('submit', this.onSubmit, this);
7504 // this was added as random key presses on the form where triggering form submit.
7505 this.el.on('keypress', function(e) {
7506 if (e.getCharCode() != 13) {
7509 // we might need to allow it for textareas.. and some other items.
7510 // check e.getTarget().
7512 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7516 Roo.log("keypress blocked");
7524 onSubmit : function(e){
7529 * Returns true if client-side validation on the form is successful.
7532 isValid : function(){
7533 var items = this.getItems();
7535 items.each(function(f){
7544 * Returns true if any fields in this form have changed since their original load.
7547 isDirty : function(){
7549 var items = this.getItems();
7550 items.each(function(f){
7560 * Performs a predefined action (submit or load) or custom actions you define on this form.
7561 * @param {String} actionName The name of the action type
7562 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7563 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7564 * accept other config options):
7566 Property Type Description
7567 ---------------- --------------- ----------------------------------------------------------------------------------
7568 url String The url for the action (defaults to the form's url)
7569 method String The form method to use (defaults to the form's method, or POST if not defined)
7570 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7571 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7572 validate the form on the client (defaults to false)
7574 * @return {BasicForm} this
7576 doAction : function(action, options){
7577 if(typeof action == 'string'){
7578 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7580 if(this.fireEvent('beforeaction', this, action) !== false){
7581 this.beforeAction(action);
7582 action.run.defer(100, action);
7588 beforeAction : function(action){
7589 var o = action.options;
7592 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7594 // not really supported yet.. ??
7596 //if(this.waitMsgTarget === true){
7597 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7598 //}else if(this.waitMsgTarget){
7599 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7600 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7602 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7608 afterAction : function(action, success){
7609 this.activeAction = null;
7610 var o = action.options;
7612 //if(this.waitMsgTarget === true){
7614 //}else if(this.waitMsgTarget){
7615 // this.waitMsgTarget.unmask();
7617 // Roo.MessageBox.updateProgress(1);
7618 // Roo.MessageBox.hide();
7625 Roo.callback(o.success, o.scope, [this, action]);
7626 this.fireEvent('actioncomplete', this, action);
7630 // failure condition..
7631 // we have a scenario where updates need confirming.
7632 // eg. if a locking scenario exists..
7633 // we look for { errors : { needs_confirm : true }} in the response.
7635 (typeof(action.result) != 'undefined') &&
7636 (typeof(action.result.errors) != 'undefined') &&
7637 (typeof(action.result.errors.needs_confirm) != 'undefined')
7640 Roo.log("not supported yet");
7643 Roo.MessageBox.confirm(
7644 "Change requires confirmation",
7645 action.result.errorMsg,
7650 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7660 Roo.callback(o.failure, o.scope, [this, action]);
7661 // show an error message if no failed handler is set..
7662 if (!this.hasListener('actionfailed')) {
7663 Roo.log("need to add dialog support");
7665 Roo.MessageBox.alert("Error",
7666 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7667 action.result.errorMsg :
7668 "Saving Failed, please check your entries or try again"
7673 this.fireEvent('actionfailed', this, action);
7678 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7679 * @param {String} id The value to search for
7682 findField : function(id){
7683 var items = this.getItems();
7684 var field = items.get(id);
7686 items.each(function(f){
7687 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7694 return field || null;
7697 * Mark fields in this form invalid in bulk.
7698 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7699 * @return {BasicForm} this
7701 markInvalid : function(errors){
7702 if(errors instanceof Array){
7703 for(var i = 0, len = errors.length; i < len; i++){
7704 var fieldError = errors[i];
7705 var f = this.findField(fieldError.id);
7707 f.markInvalid(fieldError.msg);
7713 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7714 field.markInvalid(errors[id]);
7718 //Roo.each(this.childForms || [], function (f) {
7719 // f.markInvalid(errors);
7726 * Set values for fields in this form in bulk.
7727 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7728 * @return {BasicForm} this
7730 setValues : function(values){
7731 if(values instanceof Array){ // array of objects
7732 for(var i = 0, len = values.length; i < len; i++){
7734 var f = this.findField(v.id);
7736 f.setValue(v.value);
7737 if(this.trackResetOnLoad){
7738 f.originalValue = f.getValue();
7742 }else{ // object hash
7745 if(typeof values[id] != 'function' && (field = this.findField(id))){
7747 if (field.setFromData &&
7749 field.displayField &&
7750 // combos' with local stores can
7751 // be queried via setValue()
7752 // to set their value..
7753 (field.store && !field.store.isLocal)
7757 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7758 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7759 field.setFromData(sd);
7762 field.setValue(values[id]);
7766 if(this.trackResetOnLoad){
7767 field.originalValue = field.getValue();
7773 //Roo.each(this.childForms || [], function (f) {
7774 // f.setValues(values);
7781 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7782 * they are returned as an array.
7783 * @param {Boolean} asString
7786 getValues : function(asString){
7787 //if (this.childForms) {
7788 // copy values from the child forms
7789 // Roo.each(this.childForms, function (f) {
7790 // this.setValues(f.getValues());
7796 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7797 if(asString === true){
7800 return Roo.urlDecode(fs);
7804 * Returns the fields in this form as an object with key/value pairs.
7805 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7808 getFieldValues : function(with_hidden)
7810 var items = this.getItems();
7812 items.each(function(f){
7816 var v = f.getValue();
7817 if (f.inputType =='radio') {
7818 if (typeof(ret[f.getName()]) == 'undefined') {
7819 ret[f.getName()] = ''; // empty..
7822 if (!f.el.dom.checked) {
7830 // not sure if this supported any more..
7831 if ((typeof(v) == 'object') && f.getRawValue) {
7832 v = f.getRawValue() ; // dates..
7834 // combo boxes where name != hiddenName...
7835 if (f.name != f.getName()) {
7836 ret[f.name] = f.getRawValue();
7838 ret[f.getName()] = v;
7845 * Clears all invalid messages in this form.
7846 * @return {BasicForm} this
7848 clearInvalid : function(){
7849 var items = this.getItems();
7851 items.each(function(f){
7862 * @return {BasicForm} this
7865 var items = this.getItems();
7866 items.each(function(f){
7870 Roo.each(this.childForms || [], function (f) {
7877 getItems : function()
7879 var r=new Roo.util.MixedCollection(false, function(o){
7880 return o.id || (o.id = Roo.id());
7882 var iter = function(el) {
7889 Roo.each(el.items,function(e) {
7909 * Ext JS Library 1.1.1
7910 * Copyright(c) 2006-2007, Ext JS, LLC.
7912 * Originally Released Under LGPL - original licence link has changed is not relivant.
7915 * <script type="text/javascript">
7918 * @class Roo.form.VTypes
7919 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7922 Roo.form.VTypes = function(){
7923 // closure these in so they are only created once.
7924 var alpha = /^[a-zA-Z_]+$/;
7925 var alphanum = /^[a-zA-Z0-9_]+$/;
7926 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7927 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7929 // All these messages and functions are configurable
7932 * The function used to validate email addresses
7933 * @param {String} value The email address
7935 'email' : function(v){
7936 return email.test(v);
7939 * The error text to display when the email validation function returns false
7942 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7944 * The keystroke filter mask to be applied on email input
7947 'emailMask' : /[a-z0-9_\.\-@]/i,
7950 * The function used to validate URLs
7951 * @param {String} value The URL
7953 'url' : function(v){
7957 * The error text to display when the url validation function returns false
7960 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7963 * The function used to validate alpha values
7964 * @param {String} value The value
7966 'alpha' : function(v){
7967 return alpha.test(v);
7970 * The error text to display when the alpha validation function returns false
7973 'alphaText' : 'This field should only contain letters and _',
7975 * The keystroke filter mask to be applied on alpha input
7978 'alphaMask' : /[a-z_]/i,
7981 * The function used to validate alphanumeric values
7982 * @param {String} value The value
7984 'alphanum' : function(v){
7985 return alphanum.test(v);
7988 * The error text to display when the alphanumeric validation function returns false
7991 'alphanumText' : 'This field should only contain letters, numbers and _',
7993 * The keystroke filter mask to be applied on alphanumeric input
7996 'alphanumMask' : /[a-z0-9_]/i
8006 * @class Roo.bootstrap.Input
8007 * @extends Roo.bootstrap.Component
8008 * Bootstrap Input class
8009 * @cfg {Boolean} disabled is it disabled
8010 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8011 * @cfg {String} name name of the input
8012 * @cfg {string} fieldLabel - the label associated
8013 * @cfg {string} placeholder - placeholder to put in text.
8014 * @cfg {string} before - input group add on before
8015 * @cfg {string} after - input group add on after
8016 * @cfg {string} size - (lg|sm) or leave empty..
8017 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8018 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8019 * @cfg {Number} md colspan out of 12 for computer-sized screens
8020 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8021 * @cfg {string} value default value of the input
8022 * @cfg {Number} labelWidth set the width of label (0-12)
8023 * @cfg {String} labelAlign (top|left)
8024 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8025 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8026 * @cfg {String} indicatorpos (left|right) default left
8028 * @cfg {String} align (left|center|right) Default left
8029 * @cfg {Boolean} forceFeedback (true|false) Default false
8035 * Create a new Input
8036 * @param {Object} config The config object
8039 Roo.bootstrap.Input = function(config){
8040 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8045 * Fires when this field receives input focus.
8046 * @param {Roo.form.Field} this
8051 * Fires when this field loses input focus.
8052 * @param {Roo.form.Field} this
8057 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8058 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8059 * @param {Roo.form.Field} this
8060 * @param {Roo.EventObject} e The event object
8065 * Fires just before the field blurs if the field value has changed.
8066 * @param {Roo.form.Field} this
8067 * @param {Mixed} newValue The new value
8068 * @param {Mixed} oldValue The original value
8073 * Fires after the field has been marked as invalid.
8074 * @param {Roo.form.Field} this
8075 * @param {String} msg The validation message
8080 * Fires after the field has been validated with no errors.
8081 * @param {Roo.form.Field} this
8086 * Fires after the key up
8087 * @param {Roo.form.Field} this
8088 * @param {Roo.EventObject} e The event Object
8094 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8096 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8097 automatic validation (defaults to "keyup").
8099 validationEvent : "keyup",
8101 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8103 validateOnBlur : true,
8105 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8107 validationDelay : 250,
8109 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8111 focusClass : "x-form-focus", // not needed???
8115 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8117 invalidClass : "has-warning",
8120 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8122 validClass : "has-success",
8125 * @cfg {Boolean} hasFeedback (true|false) default true
8130 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8132 invalidFeedbackClass : "glyphicon-warning-sign",
8135 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8137 validFeedbackClass : "glyphicon-ok",
8140 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8142 selectOnFocus : false,
8145 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8149 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8154 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8156 disableKeyFilter : false,
8159 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8163 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8167 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8169 blankText : "This field is required",
8172 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8176 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8178 maxLength : Number.MAX_VALUE,
8180 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8182 minLengthText : "The minimum length for this field is {0}",
8184 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8186 maxLengthText : "The maximum length for this field is {0}",
8190 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8191 * If available, this function will be called only after the basic validators all return true, and will be passed the
8192 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8196 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8197 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8198 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8202 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8206 autocomplete: false,
8225 formatedValue : false,
8226 forceFeedback : false,
8228 indicatorpos : 'left',
8230 parentLabelAlign : function()
8233 while (parent.parent()) {
8234 parent = parent.parent();
8235 if (typeof(parent.labelAlign) !='undefined') {
8236 return parent.labelAlign;
8243 getAutoCreate : function()
8245 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8251 if(this.inputType != 'hidden'){
8252 cfg.cls = 'form-group' //input-group
8258 type : this.inputType,
8260 cls : 'form-control',
8261 placeholder : this.placeholder || '',
8262 autocomplete : this.autocomplete || 'new-password'
8266 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8269 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8270 input.maxLength = this.maxLength;
8273 if (this.disabled) {
8274 input.disabled=true;
8277 if (this.readOnly) {
8278 input.readonly=true;
8282 input.name = this.name;
8286 input.cls += ' input-' + this.size;
8290 ['xs','sm','md','lg'].map(function(size){
8291 if (settings[size]) {
8292 cfg.cls += ' col-' + size + '-' + settings[size];
8296 var inputblock = input;
8300 cls: 'glyphicon form-control-feedback'
8303 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8306 cls : 'has-feedback',
8314 if (this.before || this.after) {
8317 cls : 'input-group',
8321 if (this.before && typeof(this.before) == 'string') {
8323 inputblock.cn.push({
8325 cls : 'roo-input-before input-group-addon',
8329 if (this.before && typeof(this.before) == 'object') {
8330 this.before = Roo.factory(this.before);
8332 inputblock.cn.push({
8334 cls : 'roo-input-before input-group-' +
8335 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8339 inputblock.cn.push(input);
8341 if (this.after && typeof(this.after) == 'string') {
8342 inputblock.cn.push({
8344 cls : 'roo-input-after input-group-addon',
8348 if (this.after && typeof(this.after) == 'object') {
8349 this.after = Roo.factory(this.after);
8351 inputblock.cn.push({
8353 cls : 'roo-input-after input-group-' +
8354 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8358 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8359 inputblock.cls += ' has-feedback';
8360 inputblock.cn.push(feedback);
8364 if (align ==='left' && this.fieldLabel.length) {
8369 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8370 tooltip : 'This field is required'
8375 cls : 'control-label col-sm-' + this.labelWidth,
8376 html : this.fieldLabel
8380 cls : "col-sm-" + (12 - this.labelWidth),
8388 if(this.indicatorpos == 'right'){
8393 cls : 'control-label col-sm-' + this.labelWidth,
8394 html : this.fieldLabel
8399 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8400 tooltip : 'This field is required'
8403 cls : "col-sm-" + (12 - this.labelWidth),
8412 } else if ( this.fieldLabel.length) {
8417 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8418 tooltip : 'This field is required'
8422 //cls : 'input-group-addon',
8423 html : this.fieldLabel
8431 if(this.indicatorpos == 'right'){
8436 //cls : 'input-group-addon',
8437 html : this.fieldLabel
8442 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8443 tooltip : 'This field is required'
8463 if (this.parentType === 'Navbar' && this.parent().bar) {
8464 cfg.cls += ' navbar-form';
8467 if (this.parentType === 'NavGroup') {
8468 cfg.cls += ' navbar-form';
8476 * return the real input element.
8478 inputEl: function ()
8480 return this.el.select('input.form-control',true).first();
8483 tooltipEl : function()
8485 return this.inputEl();
8488 indicatorEl : function()
8490 var indicator = this.el.select('i.roo-required-indicator',true).first();
8500 setDisabled : function(v)
8502 var i = this.inputEl().dom;
8504 i.removeAttribute('disabled');
8508 i.setAttribute('disabled','true');
8510 initEvents : function()
8513 this.inputEl().on("keydown" , this.fireKey, this);
8514 this.inputEl().on("focus", this.onFocus, this);
8515 this.inputEl().on("blur", this.onBlur, this);
8517 this.inputEl().relayEvent('keyup', this);
8519 this.indicator = this.indicatorEl();
8522 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8523 this.indicator.hide();
8526 // reference to original value for reset
8527 this.originalValue = this.getValue();
8528 //Roo.form.TextField.superclass.initEvents.call(this);
8529 if(this.validationEvent == 'keyup'){
8530 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8531 this.inputEl().on('keyup', this.filterValidation, this);
8533 else if(this.validationEvent !== false){
8534 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8537 if(this.selectOnFocus){
8538 this.on("focus", this.preFocus, this);
8541 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8542 this.inputEl().on("keypress", this.filterKeys, this);
8544 this.inputEl().relayEvent('keypress', this);
8547 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8548 this.el.on("click", this.autoSize, this);
8551 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8552 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8555 if (typeof(this.before) == 'object') {
8556 this.before.render(this.el.select('.roo-input-before',true).first());
8558 if (typeof(this.after) == 'object') {
8559 this.after.render(this.el.select('.roo-input-after',true).first());
8564 filterValidation : function(e){
8565 if(!e.isNavKeyPress()){
8566 this.validationTask.delay(this.validationDelay);
8570 * Validates the field value
8571 * @return {Boolean} True if the value is valid, else false
8573 validate : function(){
8574 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8575 if(this.disabled || this.validateValue(this.getRawValue())){
8586 * Validates a value according to the field's validation rules and marks the field as invalid
8587 * if the validation fails
8588 * @param {Mixed} value The value to validate
8589 * @return {Boolean} True if the value is valid, else false
8591 validateValue : function(value){
8592 if(value.length < 1) { // if it's blank
8593 if(this.allowBlank){
8599 if(value.length < this.minLength){
8602 if(value.length > this.maxLength){
8606 var vt = Roo.form.VTypes;
8607 if(!vt[this.vtype](value, this)){
8611 if(typeof this.validator == "function"){
8612 var msg = this.validator(value);
8618 if(this.regex && !this.regex.test(value)){
8628 fireKey : function(e){
8629 //Roo.log('field ' + e.getKey());
8630 if(e.isNavKeyPress()){
8631 this.fireEvent("specialkey", this, e);
8634 focus : function (selectText){
8636 this.inputEl().focus();
8637 if(selectText === true){
8638 this.inputEl().dom.select();
8644 onFocus : function(){
8645 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8646 // this.el.addClass(this.focusClass);
8649 this.hasFocus = true;
8650 this.startValue = this.getValue();
8651 this.fireEvent("focus", this);
8655 beforeBlur : Roo.emptyFn,
8659 onBlur : function(){
8661 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8662 //this.el.removeClass(this.focusClass);
8664 this.hasFocus = false;
8665 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8668 var v = this.getValue();
8669 if(String(v) !== String(this.startValue)){
8670 this.fireEvent('change', this, v, this.startValue);
8672 this.fireEvent("blur", this);
8676 * Resets the current field value to the originally loaded value and clears any validation messages
8679 this.setValue(this.originalValue);
8683 * Returns the name of the field
8684 * @return {Mixed} name The name field
8686 getName: function(){
8690 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8691 * @return {Mixed} value The field value
8693 getValue : function(){
8695 var v = this.inputEl().getValue();
8700 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8701 * @return {Mixed} value The field value
8703 getRawValue : function(){
8704 var v = this.inputEl().getValue();
8710 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8711 * @param {Mixed} value The value to set
8713 setRawValue : function(v){
8714 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8717 selectText : function(start, end){
8718 var v = this.getRawValue();
8720 start = start === undefined ? 0 : start;
8721 end = end === undefined ? v.length : end;
8722 var d = this.inputEl().dom;
8723 if(d.setSelectionRange){
8724 d.setSelectionRange(start, end);
8725 }else if(d.createTextRange){
8726 var range = d.createTextRange();
8727 range.moveStart("character", start);
8728 range.moveEnd("character", v.length-end);
8735 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8736 * @param {Mixed} value The value to set
8738 setValue : function(v){
8741 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8747 processValue : function(value){
8748 if(this.stripCharsRe){
8749 var newValue = value.replace(this.stripCharsRe, '');
8750 if(newValue !== value){
8751 this.setRawValue(newValue);
8758 preFocus : function(){
8760 if(this.selectOnFocus){
8761 this.inputEl().dom.select();
8764 filterKeys : function(e){
8766 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8769 var c = e.getCharCode(), cc = String.fromCharCode(c);
8770 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8773 if(!this.maskRe.test(cc)){
8778 * Clear any invalid styles/messages for this field
8780 clearInvalid : function(){
8782 if(!this.el || this.preventMark){ // not rendered
8787 this.indicator.hide();
8790 this.el.removeClass(this.invalidClass);
8792 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8794 var feedback = this.el.select('.form-control-feedback', true).first();
8797 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8802 this.fireEvent('valid', this);
8806 * Mark this field as valid
8808 markValid : function()
8810 if(!this.el || this.preventMark){ // not rendered
8814 this.el.removeClass([this.invalidClass, this.validClass]);
8816 var feedback = this.el.select('.form-control-feedback', true).first();
8819 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8822 if(this.disabled || this.allowBlank){
8827 this.indicator.hide();
8830 this.el.addClass(this.validClass);
8832 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8834 var feedback = this.el.select('.form-control-feedback', true).first();
8837 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8838 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8843 this.fireEvent('valid', this);
8847 * Mark this field as invalid
8848 * @param {String} msg The validation message
8850 markInvalid : function(msg)
8852 if(!this.el || this.preventMark){ // not rendered
8856 this.el.removeClass([this.invalidClass, this.validClass]);
8858 var feedback = this.el.select('.form-control-feedback', true).first();
8861 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8864 if(this.disabled || this.allowBlank){
8869 this.indicator.show();
8872 this.el.addClass(this.invalidClass);
8874 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8876 var feedback = this.el.select('.form-control-feedback', true).first();
8879 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8881 if(this.getValue().length || this.forceFeedback){
8882 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8889 this.fireEvent('invalid', this, msg);
8892 SafariOnKeyDown : function(event)
8894 // this is a workaround for a password hang bug on chrome/ webkit.
8896 var isSelectAll = false;
8898 if(this.inputEl().dom.selectionEnd > 0){
8899 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8901 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8902 event.preventDefault();
8907 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8909 event.preventDefault();
8910 // this is very hacky as keydown always get's upper case.
8912 var cc = String.fromCharCode(event.getCharCode());
8913 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8917 adjustWidth : function(tag, w){
8918 tag = tag.toLowerCase();
8919 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8920 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8924 if(tag == 'textarea'){
8927 }else if(Roo.isOpera){
8931 if(tag == 'textarea'){
8950 * @class Roo.bootstrap.TextArea
8951 * @extends Roo.bootstrap.Input
8952 * Bootstrap TextArea class
8953 * @cfg {Number} cols Specifies the visible width of a text area
8954 * @cfg {Number} rows Specifies the visible number of lines in a text area
8955 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8956 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8957 * @cfg {string} html text
8960 * Create a new TextArea
8961 * @param {Object} config The config object
8964 Roo.bootstrap.TextArea = function(config){
8965 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8969 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8979 getAutoCreate : function(){
8981 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8992 value : this.value || '',
8993 html: this.html || '',
8994 cls : 'form-control',
8995 placeholder : this.placeholder || ''
8999 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9000 input.maxLength = this.maxLength;
9004 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9008 input.cols = this.cols;
9011 if (this.readOnly) {
9012 input.readonly = true;
9016 input.name = this.name;
9020 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9024 ['xs','sm','md','lg'].map(function(size){
9025 if (settings[size]) {
9026 cfg.cls += ' col-' + size + '-' + settings[size];
9030 var inputblock = input;
9032 if(this.hasFeedback && !this.allowBlank){
9036 cls: 'glyphicon form-control-feedback'
9040 cls : 'has-feedback',
9049 if (this.before || this.after) {
9052 cls : 'input-group',
9056 inputblock.cn.push({
9058 cls : 'input-group-addon',
9063 inputblock.cn.push(input);
9065 if(this.hasFeedback && !this.allowBlank){
9066 inputblock.cls += ' has-feedback';
9067 inputblock.cn.push(feedback);
9071 inputblock.cn.push({
9073 cls : 'input-group-addon',
9080 if (align ==='left' && this.fieldLabel.length) {
9081 // Roo.log("left and has label");
9087 cls : 'control-label col-sm-' + this.labelWidth,
9088 html : this.fieldLabel
9092 cls : "col-sm-" + (12 - this.labelWidth),
9099 } else if ( this.fieldLabel.length) {
9100 // Roo.log(" label");
9105 //cls : 'input-group-addon',
9106 html : this.fieldLabel
9116 // Roo.log(" no label && no align");
9126 if (this.disabled) {
9127 input.disabled=true;
9134 * return the real textarea element.
9136 inputEl: function ()
9138 return this.el.select('textarea.form-control',true).first();
9142 * Clear any invalid styles/messages for this field
9144 clearInvalid : function()
9147 if(!this.el || this.preventMark){ // not rendered
9151 var label = this.el.select('label', true).first();
9152 var icon = this.el.select('i.fa-star', true).first();
9158 this.el.removeClass(this.invalidClass);
9160 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9162 var feedback = this.el.select('.form-control-feedback', true).first();
9165 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9170 this.fireEvent('valid', this);
9174 * Mark this field as valid
9176 markValid : function()
9178 if(!this.el || this.preventMark){ // not rendered
9182 this.el.removeClass([this.invalidClass, this.validClass]);
9184 var feedback = this.el.select('.form-control-feedback', true).first();
9187 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9190 if(this.disabled || this.allowBlank){
9194 var label = this.el.select('label', true).first();
9195 var icon = this.el.select('i.fa-star', true).first();
9201 this.el.addClass(this.validClass);
9203 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9205 var feedback = this.el.select('.form-control-feedback', true).first();
9208 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9209 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9214 this.fireEvent('valid', this);
9218 * Mark this field as invalid
9219 * @param {String} msg The validation message
9221 markInvalid : function(msg)
9223 if(!this.el || this.preventMark){ // not rendered
9227 this.el.removeClass([this.invalidClass, this.validClass]);
9229 var feedback = this.el.select('.form-control-feedback', true).first();
9232 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9235 if(this.disabled || this.allowBlank){
9239 var label = this.el.select('label', true).first();
9240 var icon = this.el.select('i.fa-star', true).first();
9242 if(!this.getValue().length && label && !icon){
9243 this.el.createChild({
9245 cls : 'text-danger fa fa-lg fa-star',
9246 tooltip : 'This field is required',
9247 style : 'margin-right:5px;'
9251 this.el.addClass(this.invalidClass);
9253 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9255 var feedback = this.el.select('.form-control-feedback', true).first();
9258 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9260 if(this.getValue().length || this.forceFeedback){
9261 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9268 this.fireEvent('invalid', this, msg);
9276 * trigger field - base class for combo..
9281 * @class Roo.bootstrap.TriggerField
9282 * @extends Roo.bootstrap.Input
9283 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9284 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9285 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9286 * for which you can provide a custom implementation. For example:
9288 var trigger = new Roo.bootstrap.TriggerField();
9289 trigger.onTriggerClick = myTriggerFn;
9290 trigger.applyTo('my-field');
9293 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9294 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9295 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9296 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9297 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9300 * Create a new TriggerField.
9301 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9302 * to the base TextField)
9304 Roo.bootstrap.TriggerField = function(config){
9305 this.mimicing = false;
9306 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9309 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9311 * @cfg {String} triggerClass A CSS class to apply to the trigger
9314 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9319 * @cfg {Boolean} removable (true|false) special filter default false
9323 /** @cfg {Boolean} grow @hide */
9324 /** @cfg {Number} growMin @hide */
9325 /** @cfg {Number} growMax @hide */
9331 autoSize: Roo.emptyFn,
9338 actionMode : 'wrap',
9343 getAutoCreate : function(){
9345 var align = this.labelAlign || this.parentLabelAlign();
9350 cls: 'form-group' //input-group
9357 type : this.inputType,
9358 cls : 'form-control',
9359 autocomplete: 'new-password',
9360 placeholder : this.placeholder || ''
9364 input.name = this.name;
9367 input.cls += ' input-' + this.size;
9370 if (this.disabled) {
9371 input.disabled=true;
9374 var inputblock = input;
9376 if(this.hasFeedback && !this.allowBlank){
9380 cls: 'glyphicon form-control-feedback'
9383 if(this.removable && !this.editable && !this.tickable){
9385 cls : 'has-feedback',
9391 cls : 'roo-combo-removable-btn close'
9398 cls : 'has-feedback',
9407 if(this.removable && !this.editable && !this.tickable){
9409 cls : 'roo-removable',
9415 cls : 'roo-combo-removable-btn close'
9422 if (this.before || this.after) {
9425 cls : 'input-group',
9429 inputblock.cn.push({
9431 cls : 'input-group-addon',
9436 inputblock.cn.push(input);
9438 if(this.hasFeedback && !this.allowBlank){
9439 inputblock.cls += ' has-feedback';
9440 inputblock.cn.push(feedback);
9444 inputblock.cn.push({
9446 cls : 'input-group-addon',
9459 cls: 'form-hidden-field'
9473 cls: 'form-hidden-field'
9477 cls: 'roo-select2-choices',
9481 cls: 'roo-select2-search-field',
9494 cls: 'roo-select2-container input-group',
9499 // cls: 'typeahead typeahead-long dropdown-menu',
9500 // style: 'display:none'
9505 if(!this.multiple && this.showToggleBtn){
9511 if (this.caret != false) {
9514 cls: 'fa fa-' + this.caret
9521 cls : 'input-group-addon btn dropdown-toggle',
9526 cls: 'combobox-clear',
9540 combobox.cls += ' roo-select2-container-multi';
9543 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9545 // Roo.log("left and has label");
9549 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9550 tooltip : 'This field is required'
9555 cls : 'control-label col-sm-' + this.labelWidth,
9556 html : this.fieldLabel
9560 cls : "col-sm-" + (12 - this.labelWidth),
9568 if(this.indicatorpos == 'right'){
9573 cls : 'control-label col-sm-' + this.labelWidth,
9574 html : this.fieldLabel
9579 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9580 tooltip : 'This field is required'
9583 cls : "col-sm-" + (12 - this.labelWidth),
9592 } else if ( this.fieldLabel.length) {
9593 // Roo.log(" label");
9597 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9598 tooltip : 'This field is required'
9602 //cls : 'input-group-addon',
9603 html : this.fieldLabel
9611 if(this.indicatorpos == 'right'){
9616 //cls : 'input-group-addon',
9617 html : this.fieldLabel
9622 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9623 tooltip : 'This field is required'
9634 // Roo.log(" no label && no align");
9641 ['xs','sm','md','lg'].map(function(size){
9642 if (settings[size]) {
9643 cfg.cls += ' col-' + size + '-' + settings[size];
9654 onResize : function(w, h){
9655 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9656 // if(typeof w == 'number'){
9657 // var x = w - this.trigger.getWidth();
9658 // this.inputEl().setWidth(this.adjustWidth('input', x));
9659 // this.trigger.setStyle('left', x+'px');
9664 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9667 getResizeEl : function(){
9668 return this.inputEl();
9672 getPositionEl : function(){
9673 return this.inputEl();
9677 alignErrorIcon : function(){
9678 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9682 initEvents : function(){
9686 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9687 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9688 if(!this.multiple && this.showToggleBtn){
9689 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9690 if(this.hideTrigger){
9691 this.trigger.setDisplayed(false);
9693 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9697 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9700 if(this.removable && !this.editable && !this.tickable){
9701 var close = this.closeTriggerEl();
9704 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9705 close.on('click', this.removeBtnClick, this, close);
9709 //this.trigger.addClassOnOver('x-form-trigger-over');
9710 //this.trigger.addClassOnClick('x-form-trigger-click');
9713 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9717 closeTriggerEl : function()
9719 var close = this.el.select('.roo-combo-removable-btn', true).first();
9720 return close ? close : false;
9723 removeBtnClick : function(e, h, el)
9727 if(this.fireEvent("remove", this) !== false){
9729 this.fireEvent("afterremove", this)
9733 createList : function()
9735 this.list = Roo.get(document.body).createChild({
9737 cls: 'typeahead typeahead-long dropdown-menu',
9738 style: 'display:none'
9741 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9746 initTrigger : function(){
9751 onDestroy : function(){
9753 this.trigger.removeAllListeners();
9754 // this.trigger.remove();
9757 // this.wrap.remove();
9759 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9763 onFocus : function(){
9764 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9767 this.wrap.addClass('x-trigger-wrap-focus');
9768 this.mimicing = true;
9769 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9770 if(this.monitorTab){
9771 this.el.on("keydown", this.checkTab, this);
9778 checkTab : function(e){
9779 if(e.getKey() == e.TAB){
9785 onBlur : function(){
9790 mimicBlur : function(e, t){
9792 if(!this.wrap.contains(t) && this.validateBlur()){
9799 triggerBlur : function(){
9800 this.mimicing = false;
9801 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9802 if(this.monitorTab){
9803 this.el.un("keydown", this.checkTab, this);
9805 //this.wrap.removeClass('x-trigger-wrap-focus');
9806 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9810 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9811 validateBlur : function(e, t){
9816 onDisable : function(){
9817 this.inputEl().dom.disabled = true;
9818 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9820 // this.wrap.addClass('x-item-disabled');
9825 onEnable : function(){
9826 this.inputEl().dom.disabled = false;
9827 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9829 // this.el.removeClass('x-item-disabled');
9834 onShow : function(){
9835 var ae = this.getActionEl();
9838 ae.dom.style.display = '';
9839 ae.dom.style.visibility = 'visible';
9845 onHide : function(){
9846 var ae = this.getActionEl();
9847 ae.dom.style.display = 'none';
9851 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9852 * by an implementing function.
9854 * @param {EventObject} e
9856 onTriggerClick : Roo.emptyFn
9860 * Ext JS Library 1.1.1
9861 * Copyright(c) 2006-2007, Ext JS, LLC.
9863 * Originally Released Under LGPL - original licence link has changed is not relivant.
9866 * <script type="text/javascript">
9871 * @class Roo.data.SortTypes
9873 * Defines the default sorting (casting?) comparison functions used when sorting data.
9875 Roo.data.SortTypes = {
9877 * Default sort that does nothing
9878 * @param {Mixed} s The value being converted
9879 * @return {Mixed} The comparison value
9886 * The regular expression used to strip tags
9890 stripTagsRE : /<\/?[^>]+>/gi,
9893 * Strips all HTML tags to sort on text only
9894 * @param {Mixed} s The value being converted
9895 * @return {String} The comparison value
9897 asText : function(s){
9898 return String(s).replace(this.stripTagsRE, "");
9902 * Strips all HTML tags to sort on text only - Case insensitive
9903 * @param {Mixed} s The value being converted
9904 * @return {String} The comparison value
9906 asUCText : function(s){
9907 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9911 * Case insensitive string
9912 * @param {Mixed} s The value being converted
9913 * @return {String} The comparison value
9915 asUCString : function(s) {
9916 return String(s).toUpperCase();
9921 * @param {Mixed} s The value being converted
9922 * @return {Number} The comparison value
9924 asDate : function(s) {
9928 if(s instanceof Date){
9931 return Date.parse(String(s));
9936 * @param {Mixed} s The value being converted
9937 * @return {Float} The comparison value
9939 asFloat : function(s) {
9940 var val = parseFloat(String(s).replace(/,/g, ""));
9949 * @param {Mixed} s The value being converted
9950 * @return {Number} The comparison value
9952 asInt : function(s) {
9953 var val = parseInt(String(s).replace(/,/g, ""));
9961 * Ext JS Library 1.1.1
9962 * Copyright(c) 2006-2007, Ext JS, LLC.
9964 * Originally Released Under LGPL - original licence link has changed is not relivant.
9967 * <script type="text/javascript">
9971 * @class Roo.data.Record
9972 * Instances of this class encapsulate both record <em>definition</em> information, and record
9973 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9974 * to access Records cached in an {@link Roo.data.Store} object.<br>
9976 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9977 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9980 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9982 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9983 * {@link #create}. The parameters are the same.
9984 * @param {Array} data An associative Array of data values keyed by the field name.
9985 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9986 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9987 * not specified an integer id is generated.
9989 Roo.data.Record = function(data, id){
9990 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9995 * Generate a constructor for a specific record layout.
9996 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9997 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9998 * Each field definition object may contain the following properties: <ul>
9999 * <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,
10000 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10001 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10002 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10003 * is being used, then this is a string containing the javascript expression to reference the data relative to
10004 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10005 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10006 * this may be omitted.</p></li>
10007 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10008 * <ul><li>auto (Default, implies no conversion)</li>
10013 * <li>date</li></ul></p></li>
10014 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10015 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10016 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10017 * by the Reader into an object that will be stored in the Record. It is passed the
10018 * following parameters:<ul>
10019 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10021 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10023 * <br>usage:<br><pre><code>
10024 var TopicRecord = Roo.data.Record.create(
10025 {name: 'title', mapping: 'topic_title'},
10026 {name: 'author', mapping: 'username'},
10027 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10028 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10029 {name: 'lastPoster', mapping: 'user2'},
10030 {name: 'excerpt', mapping: 'post_text'}
10033 var myNewRecord = new TopicRecord({
10034 title: 'Do my job please',
10037 lastPost: new Date(),
10038 lastPoster: 'Animal',
10039 excerpt: 'No way dude!'
10041 myStore.add(myNewRecord);
10046 Roo.data.Record.create = function(o){
10047 var f = function(){
10048 f.superclass.constructor.apply(this, arguments);
10050 Roo.extend(f, Roo.data.Record);
10051 var p = f.prototype;
10052 p.fields = new Roo.util.MixedCollection(false, function(field){
10055 for(var i = 0, len = o.length; i < len; i++){
10056 p.fields.add(new Roo.data.Field(o[i]));
10058 f.getField = function(name){
10059 return p.fields.get(name);
10064 Roo.data.Record.AUTO_ID = 1000;
10065 Roo.data.Record.EDIT = 'edit';
10066 Roo.data.Record.REJECT = 'reject';
10067 Roo.data.Record.COMMIT = 'commit';
10069 Roo.data.Record.prototype = {
10071 * Readonly flag - true if this record has been modified.
10080 join : function(store){
10081 this.store = store;
10085 * Set the named field to the specified value.
10086 * @param {String} name The name of the field to set.
10087 * @param {Object} value The value to set the field to.
10089 set : function(name, value){
10090 if(this.data[name] == value){
10094 if(!this.modified){
10095 this.modified = {};
10097 if(typeof this.modified[name] == 'undefined'){
10098 this.modified[name] = this.data[name];
10100 this.data[name] = value;
10101 if(!this.editing && this.store){
10102 this.store.afterEdit(this);
10107 * Get the value of the named field.
10108 * @param {String} name The name of the field to get the value of.
10109 * @return {Object} The value of the field.
10111 get : function(name){
10112 return this.data[name];
10116 beginEdit : function(){
10117 this.editing = true;
10118 this.modified = {};
10122 cancelEdit : function(){
10123 this.editing = false;
10124 delete this.modified;
10128 endEdit : function(){
10129 this.editing = false;
10130 if(this.dirty && this.store){
10131 this.store.afterEdit(this);
10136 * Usually called by the {@link Roo.data.Store} which owns the Record.
10137 * Rejects all changes made to the Record since either creation, or the last commit operation.
10138 * Modified fields are reverted to their original values.
10140 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10141 * of reject operations.
10143 reject : function(){
10144 var m = this.modified;
10146 if(typeof m[n] != "function"){
10147 this.data[n] = m[n];
10150 this.dirty = false;
10151 delete this.modified;
10152 this.editing = false;
10154 this.store.afterReject(this);
10159 * Usually called by the {@link Roo.data.Store} which owns the Record.
10160 * Commits all changes made to the Record since either creation, or the last commit operation.
10162 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10163 * of commit operations.
10165 commit : function(){
10166 this.dirty = false;
10167 delete this.modified;
10168 this.editing = false;
10170 this.store.afterCommit(this);
10175 hasError : function(){
10176 return this.error != null;
10180 clearError : function(){
10185 * Creates a copy of this record.
10186 * @param {String} id (optional) A new record id if you don't want to use this record's id
10189 copy : function(newId) {
10190 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10194 * Ext JS Library 1.1.1
10195 * Copyright(c) 2006-2007, Ext JS, LLC.
10197 * Originally Released Under LGPL - original licence link has changed is not relivant.
10200 * <script type="text/javascript">
10206 * @class Roo.data.Store
10207 * @extends Roo.util.Observable
10208 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10209 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10211 * 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
10212 * has no knowledge of the format of the data returned by the Proxy.<br>
10214 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10215 * instances from the data object. These records are cached and made available through accessor functions.
10217 * Creates a new Store.
10218 * @param {Object} config A config object containing the objects needed for the Store to access data,
10219 * and read the data into Records.
10221 Roo.data.Store = function(config){
10222 this.data = new Roo.util.MixedCollection(false);
10223 this.data.getKey = function(o){
10226 this.baseParams = {};
10228 this.paramNames = {
10233 "multisort" : "_multisort"
10236 if(config && config.data){
10237 this.inlineData = config.data;
10238 delete config.data;
10241 Roo.apply(this, config);
10243 if(this.reader){ // reader passed
10244 this.reader = Roo.factory(this.reader, Roo.data);
10245 this.reader.xmodule = this.xmodule || false;
10246 if(!this.recordType){
10247 this.recordType = this.reader.recordType;
10249 if(this.reader.onMetaChange){
10250 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10254 if(this.recordType){
10255 this.fields = this.recordType.prototype.fields;
10257 this.modified = [];
10261 * @event datachanged
10262 * Fires when the data cache has changed, and a widget which is using this Store
10263 * as a Record cache should refresh its view.
10264 * @param {Store} this
10266 datachanged : true,
10268 * @event metachange
10269 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10270 * @param {Store} this
10271 * @param {Object} meta The JSON metadata
10276 * Fires when Records have been added to the Store
10277 * @param {Store} this
10278 * @param {Roo.data.Record[]} records The array of Records added
10279 * @param {Number} index The index at which the record(s) were added
10284 * Fires when a Record has been removed from the Store
10285 * @param {Store} this
10286 * @param {Roo.data.Record} record The Record that was removed
10287 * @param {Number} index The index at which the record was removed
10292 * Fires when a Record has been updated
10293 * @param {Store} this
10294 * @param {Roo.data.Record} record The Record that was updated
10295 * @param {String} operation The update operation being performed. Value may be one of:
10297 Roo.data.Record.EDIT
10298 Roo.data.Record.REJECT
10299 Roo.data.Record.COMMIT
10305 * Fires when the data cache has been cleared.
10306 * @param {Store} this
10310 * @event beforeload
10311 * Fires before a request is made for a new data object. If the beforeload handler returns false
10312 * the load action will be canceled.
10313 * @param {Store} this
10314 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10318 * @event beforeloadadd
10319 * Fires after a new set of Records has been loaded.
10320 * @param {Store} this
10321 * @param {Roo.data.Record[]} records The Records that were loaded
10322 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10324 beforeloadadd : true,
10327 * Fires after a new set of Records has been loaded, before they are added to the store.
10328 * @param {Store} this
10329 * @param {Roo.data.Record[]} records The Records that were loaded
10330 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10331 * @params {Object} return from reader
10335 * @event loadexception
10336 * Fires if an exception occurs in the Proxy during loading.
10337 * Called with the signature of the Proxy's "loadexception" event.
10338 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10341 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10342 * @param {Object} load options
10343 * @param {Object} jsonData from your request (normally this contains the Exception)
10345 loadexception : true
10349 this.proxy = Roo.factory(this.proxy, Roo.data);
10350 this.proxy.xmodule = this.xmodule || false;
10351 this.relayEvents(this.proxy, ["loadexception"]);
10353 this.sortToggle = {};
10354 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10356 Roo.data.Store.superclass.constructor.call(this);
10358 if(this.inlineData){
10359 this.loadData(this.inlineData);
10360 delete this.inlineData;
10364 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10366 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10367 * without a remote query - used by combo/forms at present.
10371 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10374 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10377 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10378 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10381 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10382 * on any HTTP request
10385 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10388 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10392 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10393 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10395 remoteSort : false,
10398 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10399 * loaded or when a record is removed. (defaults to false).
10401 pruneModifiedRecords : false,
10404 lastOptions : null,
10407 * Add Records to the Store and fires the add event.
10408 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10410 add : function(records){
10411 records = [].concat(records);
10412 for(var i = 0, len = records.length; i < len; i++){
10413 records[i].join(this);
10415 var index = this.data.length;
10416 this.data.addAll(records);
10417 this.fireEvent("add", this, records, index);
10421 * Remove a Record from the Store and fires the remove event.
10422 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10424 remove : function(record){
10425 var index = this.data.indexOf(record);
10426 this.data.removeAt(index);
10427 if(this.pruneModifiedRecords){
10428 this.modified.remove(record);
10430 this.fireEvent("remove", this, record, index);
10434 * Remove all Records from the Store and fires the clear event.
10436 removeAll : function(){
10438 if(this.pruneModifiedRecords){
10439 this.modified = [];
10441 this.fireEvent("clear", this);
10445 * Inserts Records to the Store at the given index and fires the add event.
10446 * @param {Number} index The start index at which to insert the passed Records.
10447 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10449 insert : function(index, records){
10450 records = [].concat(records);
10451 for(var i = 0, len = records.length; i < len; i++){
10452 this.data.insert(index, records[i]);
10453 records[i].join(this);
10455 this.fireEvent("add", this, records, index);
10459 * Get the index within the cache of the passed Record.
10460 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10461 * @return {Number} The index of the passed Record. Returns -1 if not found.
10463 indexOf : function(record){
10464 return this.data.indexOf(record);
10468 * Get the index within the cache of the Record with the passed id.
10469 * @param {String} id The id of the Record to find.
10470 * @return {Number} The index of the Record. Returns -1 if not found.
10472 indexOfId : function(id){
10473 return this.data.indexOfKey(id);
10477 * Get the Record with the specified id.
10478 * @param {String} id The id of the Record to find.
10479 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10481 getById : function(id){
10482 return this.data.key(id);
10486 * Get the Record at the specified index.
10487 * @param {Number} index The index of the Record to find.
10488 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10490 getAt : function(index){
10491 return this.data.itemAt(index);
10495 * Returns a range of Records between specified indices.
10496 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10497 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10498 * @return {Roo.data.Record[]} An array of Records
10500 getRange : function(start, end){
10501 return this.data.getRange(start, end);
10505 storeOptions : function(o){
10506 o = Roo.apply({}, o);
10509 this.lastOptions = o;
10513 * Loads the Record cache from the configured Proxy using the configured Reader.
10515 * If using remote paging, then the first load call must specify the <em>start</em>
10516 * and <em>limit</em> properties in the options.params property to establish the initial
10517 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10519 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10520 * and this call will return before the new data has been loaded. Perform any post-processing
10521 * in a callback function, or in a "load" event handler.</strong>
10523 * @param {Object} options An object containing properties which control loading options:<ul>
10524 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10525 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10526 * passed the following arguments:<ul>
10527 * <li>r : Roo.data.Record[]</li>
10528 * <li>options: Options object from the load call</li>
10529 * <li>success: Boolean success indicator</li></ul></li>
10530 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10531 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10534 load : function(options){
10535 options = options || {};
10536 if(this.fireEvent("beforeload", this, options) !== false){
10537 this.storeOptions(options);
10538 var p = Roo.apply(options.params || {}, this.baseParams);
10539 // if meta was not loaded from remote source.. try requesting it.
10540 if (!this.reader.metaFromRemote) {
10541 p._requestMeta = 1;
10543 if(this.sortInfo && this.remoteSort){
10544 var pn = this.paramNames;
10545 p[pn["sort"]] = this.sortInfo.field;
10546 p[pn["dir"]] = this.sortInfo.direction;
10548 if (this.multiSort) {
10549 var pn = this.paramNames;
10550 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10553 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10558 * Reloads the Record cache from the configured Proxy using the configured Reader and
10559 * the options from the last load operation performed.
10560 * @param {Object} options (optional) An object containing properties which may override the options
10561 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10562 * the most recently used options are reused).
10564 reload : function(options){
10565 this.load(Roo.applyIf(options||{}, this.lastOptions));
10569 // Called as a callback by the Reader during a load operation.
10570 loadRecords : function(o, options, success){
10571 if(!o || success === false){
10572 if(success !== false){
10573 this.fireEvent("load", this, [], options, o);
10575 if(options.callback){
10576 options.callback.call(options.scope || this, [], options, false);
10580 // if data returned failure - throw an exception.
10581 if (o.success === false) {
10582 // show a message if no listener is registered.
10583 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10584 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10586 // loadmask wil be hooked into this..
10587 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10590 var r = o.records, t = o.totalRecords || r.length;
10592 this.fireEvent("beforeloadadd", this, r, options, o);
10594 if(!options || options.add !== true){
10595 if(this.pruneModifiedRecords){
10596 this.modified = [];
10598 for(var i = 0, len = r.length; i < len; i++){
10602 this.data = this.snapshot;
10603 delete this.snapshot;
10606 this.data.addAll(r);
10607 this.totalLength = t;
10609 this.fireEvent("datachanged", this);
10611 this.totalLength = Math.max(t, this.data.length+r.length);
10614 this.fireEvent("load", this, r, options, o);
10615 if(options.callback){
10616 options.callback.call(options.scope || this, r, options, true);
10622 * Loads data from a passed data block. A Reader which understands the format of the data
10623 * must have been configured in the constructor.
10624 * @param {Object} data The data block from which to read the Records. The format of the data expected
10625 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10626 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10628 loadData : function(o, append){
10629 var r = this.reader.readRecords(o);
10630 this.loadRecords(r, {add: append}, true);
10634 * Gets the number of cached records.
10636 * <em>If using paging, this may not be the total size of the dataset. If the data object
10637 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10638 * the data set size</em>
10640 getCount : function(){
10641 return this.data.length || 0;
10645 * Gets the total number of records in the dataset as returned by the server.
10647 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10648 * the dataset size</em>
10650 getTotalCount : function(){
10651 return this.totalLength || 0;
10655 * Returns the sort state of the Store as an object with two properties:
10657 field {String} The name of the field by which the Records are sorted
10658 direction {String} The sort order, "ASC" or "DESC"
10661 getSortState : function(){
10662 return this.sortInfo;
10666 applySort : function(){
10667 if(this.sortInfo && !this.remoteSort){
10668 var s = this.sortInfo, f = s.field;
10669 var st = this.fields.get(f).sortType;
10670 var fn = function(r1, r2){
10671 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10672 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10674 this.data.sort(s.direction, fn);
10675 if(this.snapshot && this.snapshot != this.data){
10676 this.snapshot.sort(s.direction, fn);
10682 * Sets the default sort column and order to be used by the next load operation.
10683 * @param {String} fieldName The name of the field to sort by.
10684 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10686 setDefaultSort : function(field, dir){
10687 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10691 * Sort the Records.
10692 * If remote sorting is used, the sort is performed on the server, and the cache is
10693 * reloaded. If local sorting is used, the cache is sorted internally.
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 sort : function(fieldName, dir){
10698 var f = this.fields.get(fieldName);
10700 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10702 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10703 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10708 this.sortToggle[f.name] = dir;
10709 this.sortInfo = {field: f.name, direction: dir};
10710 if(!this.remoteSort){
10712 this.fireEvent("datachanged", this);
10714 this.load(this.lastOptions);
10719 * Calls the specified function for each of the Records in the cache.
10720 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10721 * Returning <em>false</em> aborts and exits the iteration.
10722 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10724 each : function(fn, scope){
10725 this.data.each(fn, scope);
10729 * Gets all records modified since the last commit. Modified records are persisted across load operations
10730 * (e.g., during paging).
10731 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10733 getModifiedRecords : function(){
10734 return this.modified;
10738 createFilterFn : function(property, value, anyMatch){
10739 if(!value.exec){ // not a regex
10740 value = String(value);
10741 if(value.length == 0){
10744 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10746 return function(r){
10747 return value.test(r.data[property]);
10752 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10753 * @param {String} property A field on your records
10754 * @param {Number} start The record index to start at (defaults to 0)
10755 * @param {Number} end The last record index to include (defaults to length - 1)
10756 * @return {Number} The sum
10758 sum : function(property, start, end){
10759 var rs = this.data.items, v = 0;
10760 start = start || 0;
10761 end = (end || end === 0) ? end : rs.length-1;
10763 for(var i = start; i <= end; i++){
10764 v += (rs[i].data[property] || 0);
10770 * Filter the records by a specified property.
10771 * @param {String} field A field on your records
10772 * @param {String/RegExp} value Either a string that the field
10773 * should start with or a RegExp to test against the field
10774 * @param {Boolean} anyMatch True to match any part not just the beginning
10776 filter : function(property, value, anyMatch){
10777 var fn = this.createFilterFn(property, value, anyMatch);
10778 return fn ? this.filterBy(fn) : this.clearFilter();
10782 * Filter by a function. The specified function will be called with each
10783 * record in this data source. If the function returns true the record is included,
10784 * otherwise it is filtered.
10785 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10786 * @param {Object} scope (optional) The scope of the function (defaults to this)
10788 filterBy : function(fn, scope){
10789 this.snapshot = this.snapshot || this.data;
10790 this.data = this.queryBy(fn, scope||this);
10791 this.fireEvent("datachanged", this);
10795 * Query the records by a specified property.
10796 * @param {String} field A field on your records
10797 * @param {String/RegExp} value Either a string that the field
10798 * should start with or a RegExp to test against the field
10799 * @param {Boolean} anyMatch True to match any part not just the beginning
10800 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10802 query : function(property, value, anyMatch){
10803 var fn = this.createFilterFn(property, value, anyMatch);
10804 return fn ? this.queryBy(fn) : this.data.clone();
10808 * Query by a function. The specified function will be called with each
10809 * record in this data source. If the function returns true the record is included
10811 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10812 * @param {Object} scope (optional) The scope of the function (defaults to this)
10813 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10815 queryBy : function(fn, scope){
10816 var data = this.snapshot || this.data;
10817 return data.filterBy(fn, scope||this);
10821 * Collects unique values for a particular dataIndex from this store.
10822 * @param {String} dataIndex The property to collect
10823 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10824 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10825 * @return {Array} An array of the unique values
10827 collect : function(dataIndex, allowNull, bypassFilter){
10828 var d = (bypassFilter === true && this.snapshot) ?
10829 this.snapshot.items : this.data.items;
10830 var v, sv, r = [], l = {};
10831 for(var i = 0, len = d.length; i < len; i++){
10832 v = d[i].data[dataIndex];
10834 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10843 * Revert to a view of the Record cache with no filtering applied.
10844 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10846 clearFilter : function(suppressEvent){
10847 if(this.snapshot && this.snapshot != this.data){
10848 this.data = this.snapshot;
10849 delete this.snapshot;
10850 if(suppressEvent !== true){
10851 this.fireEvent("datachanged", this);
10857 afterEdit : function(record){
10858 if(this.modified.indexOf(record) == -1){
10859 this.modified.push(record);
10861 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10865 afterReject : function(record){
10866 this.modified.remove(record);
10867 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10871 afterCommit : function(record){
10872 this.modified.remove(record);
10873 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10877 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10878 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10880 commitChanges : function(){
10881 var m = this.modified.slice(0);
10882 this.modified = [];
10883 for(var i = 0, len = m.length; i < len; i++){
10889 * Cancel outstanding changes on all changed records.
10891 rejectChanges : function(){
10892 var m = this.modified.slice(0);
10893 this.modified = [];
10894 for(var i = 0, len = m.length; i < len; i++){
10899 onMetaChange : function(meta, rtype, o){
10900 this.recordType = rtype;
10901 this.fields = rtype.prototype.fields;
10902 delete this.snapshot;
10903 this.sortInfo = meta.sortInfo || this.sortInfo;
10904 this.modified = [];
10905 this.fireEvent('metachange', this, this.reader.meta);
10908 moveIndex : function(data, type)
10910 var index = this.indexOf(data);
10912 var newIndex = index + type;
10916 this.insert(newIndex, data);
10921 * Ext JS Library 1.1.1
10922 * Copyright(c) 2006-2007, Ext JS, LLC.
10924 * Originally Released Under LGPL - original licence link has changed is not relivant.
10927 * <script type="text/javascript">
10931 * @class Roo.data.SimpleStore
10932 * @extends Roo.data.Store
10933 * Small helper class to make creating Stores from Array data easier.
10934 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10935 * @cfg {Array} fields An array of field definition objects, or field name strings.
10936 * @cfg {Array} data The multi-dimensional array of data
10938 * @param {Object} config
10940 Roo.data.SimpleStore = function(config){
10941 Roo.data.SimpleStore.superclass.constructor.call(this, {
10943 reader: new Roo.data.ArrayReader({
10946 Roo.data.Record.create(config.fields)
10948 proxy : new Roo.data.MemoryProxy(config.data)
10952 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10954 * Ext JS Library 1.1.1
10955 * Copyright(c) 2006-2007, Ext JS, LLC.
10957 * Originally Released Under LGPL - original licence link has changed is not relivant.
10960 * <script type="text/javascript">
10965 * @extends Roo.data.Store
10966 * @class Roo.data.JsonStore
10967 * Small helper class to make creating Stores for JSON data easier. <br/>
10969 var store = new Roo.data.JsonStore({
10970 url: 'get-images.php',
10972 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10975 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10976 * JsonReader and HttpProxy (unless inline data is provided).</b>
10977 * @cfg {Array} fields An array of field definition objects, or field name strings.
10979 * @param {Object} config
10981 Roo.data.JsonStore = function(c){
10982 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10983 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10984 reader: new Roo.data.JsonReader(c, c.fields)
10987 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10989 * Ext JS Library 1.1.1
10990 * Copyright(c) 2006-2007, Ext JS, LLC.
10992 * Originally Released Under LGPL - original licence link has changed is not relivant.
10995 * <script type="text/javascript">
10999 Roo.data.Field = function(config){
11000 if(typeof config == "string"){
11001 config = {name: config};
11003 Roo.apply(this, config);
11006 this.type = "auto";
11009 var st = Roo.data.SortTypes;
11010 // named sortTypes are supported, here we look them up
11011 if(typeof this.sortType == "string"){
11012 this.sortType = st[this.sortType];
11015 // set default sortType for strings and dates
11016 if(!this.sortType){
11019 this.sortType = st.asUCString;
11022 this.sortType = st.asDate;
11025 this.sortType = st.none;
11030 var stripRe = /[\$,%]/g;
11032 // prebuilt conversion function for this field, instead of
11033 // switching every time we're reading a value
11035 var cv, dateFormat = this.dateFormat;
11040 cv = function(v){ return v; };
11043 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11047 return v !== undefined && v !== null && v !== '' ?
11048 parseInt(String(v).replace(stripRe, ""), 10) : '';
11053 return v !== undefined && v !== null && v !== '' ?
11054 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11059 cv = function(v){ return v === true || v === "true" || v == 1; };
11066 if(v instanceof Date){
11070 if(dateFormat == "timestamp"){
11071 return new Date(v*1000);
11073 return Date.parseDate(v, dateFormat);
11075 var parsed = Date.parse(v);
11076 return parsed ? new Date(parsed) : null;
11085 Roo.data.Field.prototype = {
11093 * Ext JS Library 1.1.1
11094 * Copyright(c) 2006-2007, Ext JS, LLC.
11096 * Originally Released Under LGPL - original licence link has changed is not relivant.
11099 * <script type="text/javascript">
11102 // Base class for reading structured data from a data source. This class is intended to be
11103 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11106 * @class Roo.data.DataReader
11107 * Base class for reading structured data from a data source. This class is intended to be
11108 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11111 Roo.data.DataReader = function(meta, recordType){
11115 this.recordType = recordType instanceof Array ?
11116 Roo.data.Record.create(recordType) : recordType;
11119 Roo.data.DataReader.prototype = {
11121 * Create an empty record
11122 * @param {Object} data (optional) - overlay some values
11123 * @return {Roo.data.Record} record created.
11125 newRow : function(d) {
11127 this.recordType.prototype.fields.each(function(c) {
11129 case 'int' : da[c.name] = 0; break;
11130 case 'date' : da[c.name] = new Date(); break;
11131 case 'float' : da[c.name] = 0.0; break;
11132 case 'boolean' : da[c.name] = false; break;
11133 default : da[c.name] = ""; break;
11137 return new this.recordType(Roo.apply(da, d));
11142 * Ext JS Library 1.1.1
11143 * Copyright(c) 2006-2007, Ext JS, LLC.
11145 * Originally Released Under LGPL - original licence link has changed is not relivant.
11148 * <script type="text/javascript">
11152 * @class Roo.data.DataProxy
11153 * @extends Roo.data.Observable
11154 * This class is an abstract base class for implementations which provide retrieval of
11155 * unformatted data objects.<br>
11157 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11158 * (of the appropriate type which knows how to parse the data object) to provide a block of
11159 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11161 * Custom implementations must implement the load method as described in
11162 * {@link Roo.data.HttpProxy#load}.
11164 Roo.data.DataProxy = function(){
11167 * @event beforeload
11168 * Fires before a network request is made to retrieve a data object.
11169 * @param {Object} This DataProxy object.
11170 * @param {Object} params The params parameter to the load function.
11175 * Fires before the load method's callback is called.
11176 * @param {Object} This DataProxy object.
11177 * @param {Object} o The data object.
11178 * @param {Object} arg The callback argument object passed to the load function.
11182 * @event loadexception
11183 * Fires if an Exception occurs during data retrieval.
11184 * @param {Object} This DataProxy object.
11185 * @param {Object} o The data object.
11186 * @param {Object} arg The callback argument object passed to the load function.
11187 * @param {Object} e The Exception.
11189 loadexception : true
11191 Roo.data.DataProxy.superclass.constructor.call(this);
11194 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11197 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11201 * Ext JS Library 1.1.1
11202 * Copyright(c) 2006-2007, Ext JS, LLC.
11204 * Originally Released Under LGPL - original licence link has changed is not relivant.
11207 * <script type="text/javascript">
11210 * @class Roo.data.MemoryProxy
11211 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11212 * to the Reader when its load method is called.
11214 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11216 Roo.data.MemoryProxy = function(data){
11220 Roo.data.MemoryProxy.superclass.constructor.call(this);
11224 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11227 * Load data from the requested source (in this case an in-memory
11228 * data object passed to the constructor), read the data object into
11229 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11230 * process that block using the passed callback.
11231 * @param {Object} params This parameter is not used by the MemoryProxy class.
11232 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11233 * object into a block of Roo.data.Records.
11234 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11235 * The function must be passed <ul>
11236 * <li>The Record block object</li>
11237 * <li>The "arg" argument from the load function</li>
11238 * <li>A boolean success indicator</li>
11240 * @param {Object} scope The scope in which to call the callback
11241 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11243 load : function(params, reader, callback, scope, arg){
11244 params = params || {};
11247 result = reader.readRecords(this.data);
11249 this.fireEvent("loadexception", this, arg, null, e);
11250 callback.call(scope, null, arg, false);
11253 callback.call(scope, result, arg, true);
11257 update : function(params, records){
11262 * Ext JS Library 1.1.1
11263 * Copyright(c) 2006-2007, Ext JS, LLC.
11265 * Originally Released Under LGPL - original licence link has changed is not relivant.
11268 * <script type="text/javascript">
11271 * @class Roo.data.HttpProxy
11272 * @extends Roo.data.DataProxy
11273 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11274 * configured to reference a certain URL.<br><br>
11276 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11277 * from which the running page was served.<br><br>
11279 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11281 * Be aware that to enable the browser to parse an XML document, the server must set
11282 * the Content-Type header in the HTTP response to "text/xml".
11284 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11285 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11286 * will be used to make the request.
11288 Roo.data.HttpProxy = function(conn){
11289 Roo.data.HttpProxy.superclass.constructor.call(this);
11290 // is conn a conn config or a real conn?
11292 this.useAjax = !conn || !conn.events;
11296 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11297 // thse are take from connection...
11300 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11303 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11304 * extra parameters to each request made by this object. (defaults to undefined)
11307 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11308 * to each request made by this object. (defaults to undefined)
11311 * @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)
11314 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11317 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11323 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11327 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11328 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11329 * a finer-grained basis than the DataProxy events.
11331 getConnection : function(){
11332 return this.useAjax ? Roo.Ajax : this.conn;
11336 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11337 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11338 * process that block using the passed callback.
11339 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11340 * for the request to the remote server.
11341 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11342 * object into a block of Roo.data.Records.
11343 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11344 * The function must be passed <ul>
11345 * <li>The Record block object</li>
11346 * <li>The "arg" argument from the load function</li>
11347 * <li>A boolean success indicator</li>
11349 * @param {Object} scope The scope in which to call the callback
11350 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11352 load : function(params, reader, callback, scope, arg){
11353 if(this.fireEvent("beforeload", this, params) !== false){
11355 params : params || {},
11357 callback : callback,
11362 callback : this.loadResponse,
11366 Roo.applyIf(o, this.conn);
11367 if(this.activeRequest){
11368 Roo.Ajax.abort(this.activeRequest);
11370 this.activeRequest = Roo.Ajax.request(o);
11372 this.conn.request(o);
11375 callback.call(scope||this, null, arg, false);
11380 loadResponse : function(o, success, response){
11381 delete this.activeRequest;
11383 this.fireEvent("loadexception", this, o, response);
11384 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11389 result = o.reader.read(response);
11391 this.fireEvent("loadexception", this, o, response, e);
11392 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11396 this.fireEvent("load", this, o, o.request.arg);
11397 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11401 update : function(dataSet){
11406 updateResponse : function(dataSet){
11411 * Ext JS Library 1.1.1
11412 * Copyright(c) 2006-2007, Ext JS, LLC.
11414 * Originally Released Under LGPL - original licence link has changed is not relivant.
11417 * <script type="text/javascript">
11421 * @class Roo.data.ScriptTagProxy
11422 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11423 * other than the originating domain of the running page.<br><br>
11425 * <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
11426 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11428 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11429 * source code that is used as the source inside a <script> tag.<br><br>
11431 * In order for the browser to process the returned data, the server must wrap the data object
11432 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11433 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11434 * depending on whether the callback name was passed:
11437 boolean scriptTag = false;
11438 String cb = request.getParameter("callback");
11441 response.setContentType("text/javascript");
11443 response.setContentType("application/x-json");
11445 Writer out = response.getWriter();
11447 out.write(cb + "(");
11449 out.print(dataBlock.toJsonString());
11456 * @param {Object} config A configuration object.
11458 Roo.data.ScriptTagProxy = function(config){
11459 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11460 Roo.apply(this, config);
11461 this.head = document.getElementsByTagName("head")[0];
11464 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11466 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11468 * @cfg {String} url The URL from which to request the data object.
11471 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11475 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11476 * the server the name of the callback function set up by the load call to process the returned data object.
11477 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11478 * javascript output which calls this named function passing the data object as its only parameter.
11480 callbackParam : "callback",
11482 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11483 * name to the request.
11488 * Load data from the configured URL, read the data object into
11489 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11490 * process that block using the passed callback.
11491 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11492 * for the request to the remote server.
11493 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11494 * object into a block of Roo.data.Records.
11495 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11496 * The function must be passed <ul>
11497 * <li>The Record block object</li>
11498 * <li>The "arg" argument from the load function</li>
11499 * <li>A boolean success indicator</li>
11501 * @param {Object} scope The scope in which to call the callback
11502 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11504 load : function(params, reader, callback, scope, arg){
11505 if(this.fireEvent("beforeload", this, params) !== false){
11507 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11509 var url = this.url;
11510 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11512 url += "&_dc=" + (new Date().getTime());
11514 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11517 cb : "stcCallback"+transId,
11518 scriptId : "stcScript"+transId,
11522 callback : callback,
11528 window[trans.cb] = function(o){
11529 conn.handleResponse(o, trans);
11532 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11534 if(this.autoAbort !== false){
11538 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11540 var script = document.createElement("script");
11541 script.setAttribute("src", url);
11542 script.setAttribute("type", "text/javascript");
11543 script.setAttribute("id", trans.scriptId);
11544 this.head.appendChild(script);
11546 this.trans = trans;
11548 callback.call(scope||this, null, arg, false);
11553 isLoading : function(){
11554 return this.trans ? true : false;
11558 * Abort the current server request.
11560 abort : function(){
11561 if(this.isLoading()){
11562 this.destroyTrans(this.trans);
11567 destroyTrans : function(trans, isLoaded){
11568 this.head.removeChild(document.getElementById(trans.scriptId));
11569 clearTimeout(trans.timeoutId);
11571 window[trans.cb] = undefined;
11573 delete window[trans.cb];
11576 // if hasn't been loaded, wait for load to remove it to prevent script error
11577 window[trans.cb] = function(){
11578 window[trans.cb] = undefined;
11580 delete window[trans.cb];
11587 handleResponse : function(o, trans){
11588 this.trans = false;
11589 this.destroyTrans(trans, true);
11592 result = trans.reader.readRecords(o);
11594 this.fireEvent("loadexception", this, o, trans.arg, e);
11595 trans.callback.call(trans.scope||window, null, trans.arg, false);
11598 this.fireEvent("load", this, o, trans.arg);
11599 trans.callback.call(trans.scope||window, result, trans.arg, true);
11603 handleFailure : function(trans){
11604 this.trans = false;
11605 this.destroyTrans(trans, false);
11606 this.fireEvent("loadexception", this, null, trans.arg);
11607 trans.callback.call(trans.scope||window, null, trans.arg, false);
11611 * Ext JS Library 1.1.1
11612 * Copyright(c) 2006-2007, Ext JS, LLC.
11614 * Originally Released Under LGPL - original licence link has changed is not relivant.
11617 * <script type="text/javascript">
11621 * @class Roo.data.JsonReader
11622 * @extends Roo.data.DataReader
11623 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11624 * based on mappings in a provided Roo.data.Record constructor.
11626 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11627 * in the reply previously.
11632 var RecordDef = Roo.data.Record.create([
11633 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11634 {name: 'occupation'} // This field will use "occupation" as the mapping.
11636 var myReader = new Roo.data.JsonReader({
11637 totalProperty: "results", // The property which contains the total dataset size (optional)
11638 root: "rows", // The property which contains an Array of row objects
11639 id: "id" // The property within each row object that provides an ID for the record (optional)
11643 * This would consume a JSON file like this:
11645 { 'results': 2, 'rows': [
11646 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11647 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11650 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11651 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11652 * paged from the remote server.
11653 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11654 * @cfg {String} root name of the property which contains the Array of row objects.
11655 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11656 * @cfg {Array} fields Array of field definition objects
11658 * Create a new JsonReader
11659 * @param {Object} meta Metadata configuration options
11660 * @param {Object} recordType Either an Array of field definition objects,
11661 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11663 Roo.data.JsonReader = function(meta, recordType){
11666 // set some defaults:
11667 Roo.applyIf(meta, {
11668 totalProperty: 'total',
11669 successProperty : 'success',
11674 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11676 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11679 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11680 * Used by Store query builder to append _requestMeta to params.
11683 metaFromRemote : false,
11685 * This method is only used by a DataProxy which has retrieved data from a remote server.
11686 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11687 * @return {Object} data A data block which is used by an Roo.data.Store object as
11688 * a cache of Roo.data.Records.
11690 read : function(response){
11691 var json = response.responseText;
11693 var o = /* eval:var:o */ eval("("+json+")");
11695 throw {message: "JsonReader.read: Json object not found"};
11701 this.metaFromRemote = true;
11702 this.meta = o.metaData;
11703 this.recordType = Roo.data.Record.create(o.metaData.fields);
11704 this.onMetaChange(this.meta, this.recordType, o);
11706 return this.readRecords(o);
11709 // private function a store will implement
11710 onMetaChange : function(meta, recordType, o){
11717 simpleAccess: function(obj, subsc) {
11724 getJsonAccessor: function(){
11726 return function(expr) {
11728 return(re.test(expr))
11729 ? new Function("obj", "return obj." + expr)
11734 return Roo.emptyFn;
11739 * Create a data block containing Roo.data.Records from an XML document.
11740 * @param {Object} o An object which contains an Array of row objects in the property specified
11741 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11742 * which contains the total size of the dataset.
11743 * @return {Object} data A data block which is used by an Roo.data.Store object as
11744 * a cache of Roo.data.Records.
11746 readRecords : function(o){
11748 * After any data loads, the raw JSON data is available for further custom processing.
11752 var s = this.meta, Record = this.recordType,
11753 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11755 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11757 if(s.totalProperty) {
11758 this.getTotal = this.getJsonAccessor(s.totalProperty);
11760 if(s.successProperty) {
11761 this.getSuccess = this.getJsonAccessor(s.successProperty);
11763 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11765 var g = this.getJsonAccessor(s.id);
11766 this.getId = function(rec) {
11768 return (r === undefined || r === "") ? null : r;
11771 this.getId = function(){return null;};
11774 for(var jj = 0; jj < fl; jj++){
11776 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11777 this.ef[jj] = this.getJsonAccessor(map);
11781 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11782 if(s.totalProperty){
11783 var vt = parseInt(this.getTotal(o), 10);
11788 if(s.successProperty){
11789 var vs = this.getSuccess(o);
11790 if(vs === false || vs === 'false'){
11795 for(var i = 0; i < c; i++){
11798 var id = this.getId(n);
11799 for(var j = 0; j < fl; j++){
11801 var v = this.ef[j](n);
11803 Roo.log('missing convert for ' + f.name);
11807 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11809 var record = new Record(values, id);
11811 records[i] = record;
11817 totalRecords : totalRecords
11822 * Ext JS Library 1.1.1
11823 * Copyright(c) 2006-2007, Ext JS, LLC.
11825 * Originally Released Under LGPL - original licence link has changed is not relivant.
11828 * <script type="text/javascript">
11832 * @class Roo.data.ArrayReader
11833 * @extends Roo.data.DataReader
11834 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11835 * Each element of that Array represents a row of data fields. The
11836 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11837 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11841 var RecordDef = Roo.data.Record.create([
11842 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11843 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11845 var myReader = new Roo.data.ArrayReader({
11846 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11850 * This would consume an Array like this:
11852 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11854 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11856 * Create a new JsonReader
11857 * @param {Object} meta Metadata configuration options.
11858 * @param {Object} recordType Either an Array of field definition objects
11859 * as specified to {@link Roo.data.Record#create},
11860 * or an {@link Roo.data.Record} object
11861 * created using {@link Roo.data.Record#create}.
11863 Roo.data.ArrayReader = function(meta, recordType){
11864 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11867 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11869 * Create a data block containing Roo.data.Records from an XML document.
11870 * @param {Object} o An Array of row objects which represents the dataset.
11871 * @return {Object} data A data block which is used by an Roo.data.Store object as
11872 * a cache of Roo.data.Records.
11874 readRecords : function(o){
11875 var sid = this.meta ? this.meta.id : null;
11876 var recordType = this.recordType, fields = recordType.prototype.fields;
11879 for(var i = 0; i < root.length; i++){
11882 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11883 for(var j = 0, jlen = fields.length; j < jlen; j++){
11884 var f = fields.items[j];
11885 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11886 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11888 values[f.name] = v;
11890 var record = new recordType(values, id);
11892 records[records.length] = record;
11896 totalRecords : records.length
11905 * @class Roo.bootstrap.ComboBox
11906 * @extends Roo.bootstrap.TriggerField
11907 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11908 * @cfg {Boolean} append (true|false) default false
11909 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11910 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11911 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11912 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11913 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11914 * @cfg {Boolean} animate default true
11915 * @cfg {Boolean} emptyResultText only for touch device
11916 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11918 * Create a new ComboBox.
11919 * @param {Object} config Configuration options
11921 Roo.bootstrap.ComboBox = function(config){
11922 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11926 * Fires when the dropdown list is expanded
11927 * @param {Roo.bootstrap.ComboBox} combo This combo box
11932 * Fires when the dropdown list is collapsed
11933 * @param {Roo.bootstrap.ComboBox} combo This combo box
11937 * @event beforeselect
11938 * Fires before a list item is selected. Return false to cancel the selection.
11939 * @param {Roo.bootstrap.ComboBox} combo This combo box
11940 * @param {Roo.data.Record} record The data record returned from the underlying store
11941 * @param {Number} index The index of the selected item in the dropdown list
11943 'beforeselect' : true,
11946 * Fires when a list item is selected
11947 * @param {Roo.bootstrap.ComboBox} combo This combo box
11948 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11949 * @param {Number} index The index of the selected item in the dropdown list
11953 * @event beforequery
11954 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11955 * The event object passed has these properties:
11956 * @param {Roo.bootstrap.ComboBox} combo This combo box
11957 * @param {String} query The query
11958 * @param {Boolean} forceAll true to force "all" query
11959 * @param {Boolean} cancel true to cancel the query
11960 * @param {Object} e The query event object
11962 'beforequery': true,
11965 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11966 * @param {Roo.bootstrap.ComboBox} combo This combo box
11971 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11972 * @param {Roo.bootstrap.ComboBox} combo This combo box
11973 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11978 * Fires when the remove value from the combobox array
11979 * @param {Roo.bootstrap.ComboBox} combo This combo box
11983 * @event afterremove
11984 * Fires when the remove value from the combobox array
11985 * @param {Roo.bootstrap.ComboBox} combo This combo box
11987 'afterremove' : true,
11989 * @event specialfilter
11990 * Fires when specialfilter
11991 * @param {Roo.bootstrap.ComboBox} combo This combo box
11993 'specialfilter' : true,
11996 * Fires when tick the element
11997 * @param {Roo.bootstrap.ComboBox} combo This combo box
12001 * @event touchviewdisplay
12002 * Fires when touch view require special display (default is using displayField)
12003 * @param {Roo.bootstrap.ComboBox} combo This combo box
12004 * @param {Object} cfg set html .
12006 'touchviewdisplay' : true
12011 this.tickItems = [];
12013 this.selectedIndex = -1;
12014 if(this.mode == 'local'){
12015 if(config.queryDelay === undefined){
12016 this.queryDelay = 10;
12018 if(config.minChars === undefined){
12024 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12027 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12028 * rendering into an Roo.Editor, defaults to false)
12031 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12032 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12035 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12038 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12039 * the dropdown list (defaults to undefined, with no header element)
12043 * @cfg {String/Roo.Template} tpl The template to use to render the output
12047 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12049 listWidth: undefined,
12051 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12052 * mode = 'remote' or 'text' if mode = 'local')
12054 displayField: undefined,
12057 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12058 * mode = 'remote' or 'value' if mode = 'local').
12059 * Note: use of a valueField requires the user make a selection
12060 * in order for a value to be mapped.
12062 valueField: undefined,
12064 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12069 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12070 * field's data value (defaults to the underlying DOM element's name)
12072 hiddenName: undefined,
12074 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12078 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12080 selectedClass: 'active',
12083 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12087 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12088 * anchor positions (defaults to 'tl-bl')
12090 listAlign: 'tl-bl?',
12092 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12096 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12097 * query specified by the allQuery config option (defaults to 'query')
12099 triggerAction: 'query',
12101 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12102 * (defaults to 4, does not apply if editable = false)
12106 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12107 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12111 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12112 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12116 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12117 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12121 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12122 * when editable = true (defaults to false)
12124 selectOnFocus:false,
12126 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12128 queryParam: 'query',
12130 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12131 * when mode = 'remote' (defaults to 'Loading...')
12133 loadingText: 'Loading...',
12135 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12139 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12143 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12144 * traditional select (defaults to true)
12148 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12152 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12156 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12157 * listWidth has a higher value)
12161 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12162 * allow the user to set arbitrary text into the field (defaults to false)
12164 forceSelection:false,
12166 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12167 * if typeAhead = true (defaults to 250)
12169 typeAheadDelay : 250,
12171 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12172 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12174 valueNotFoundText : undefined,
12176 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12178 blockFocus : false,
12181 * @cfg {Boolean} disableClear Disable showing of clear button.
12183 disableClear : false,
12185 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12187 alwaysQuery : false,
12190 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12195 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12197 invalidClass : "has-warning",
12200 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12202 validClass : "has-success",
12205 * @cfg {Boolean} specialFilter (true|false) special filter default false
12207 specialFilter : false,
12210 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12212 mobileTouchView : true,
12224 btnPosition : 'right',
12225 triggerList : true,
12226 showToggleBtn : true,
12228 emptyResultText: 'Empty',
12229 triggerText : 'Select',
12231 // element that contains real text value.. (when hidden is used..)
12233 getAutoCreate : function()
12241 if(Roo.isTouch && this.mobileTouchView){
12242 cfg = this.getAutoCreateTouchView();
12249 if(!this.tickable){
12250 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12255 * ComboBox with tickable selections
12258 var align = this.labelAlign || this.parentLabelAlign();
12261 cls : 'form-group roo-combobox-tickable' //input-group
12266 cls : 'tickable-buttons',
12271 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12272 html : this.triggerText
12278 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12285 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12292 buttons.cn.unshift({
12294 cls: 'roo-select2-search-field-input'
12300 Roo.each(buttons.cn, function(c){
12302 c.cls += ' btn-' + _this.size;
12305 if (_this.disabled) {
12316 cls: 'form-hidden-field'
12320 cls: 'roo-select2-choices',
12324 cls: 'roo-select2-search-field',
12336 cls: 'roo-select2-container input-group roo-select2-container-multi',
12341 // cls: 'typeahead typeahead-long dropdown-menu',
12342 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12347 if(this.hasFeedback && !this.allowBlank){
12351 cls: 'glyphicon form-control-feedback'
12354 combobox.cn.push(feedback);
12357 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12359 // Roo.log("left and has label");
12363 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12364 tooltip : 'This field is required'
12369 cls : 'control-label col-sm-' + this.labelWidth,
12370 html : this.fieldLabel
12374 cls : "col-sm-" + (12 - this.labelWidth),
12382 if(this.indicatorpos == 'right'){
12388 cls : 'control-label col-sm-' + this.labelWidth,
12389 html : this.fieldLabel
12394 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12395 tooltip : 'This field is required'
12398 cls : "col-sm-" + (12 - this.labelWidth),
12409 } else if ( this.fieldLabel.length) {
12410 // Roo.log(" label");
12414 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12415 tooltip : 'This field is required'
12419 //cls : 'input-group-addon',
12420 html : this.fieldLabel
12428 if(this.indicatorpos == 'right'){
12433 //cls : 'input-group-addon',
12434 html : this.fieldLabel
12440 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12441 tooltip : 'This field is required'
12452 // Roo.log(" no label && no align");
12459 ['xs','sm','md','lg'].map(function(size){
12460 if (settings[size]) {
12461 cfg.cls += ' col-' + size + '-' + settings[size];
12469 _initEventsCalled : false,
12472 initEvents: function()
12475 if (this._initEventsCalled) { // as we call render... prevent looping...
12478 this._initEventsCalled = true;
12481 throw "can not find store for combo";
12484 this.store = Roo.factory(this.store, Roo.data);
12486 // if we are building from html. then this element is so complex, that we can not really
12487 // use the rendered HTML.
12488 // so we have to trash and replace the previous code.
12489 if (Roo.XComponent.build_from_html) {
12491 // remove this element....
12492 var e = this.el.dom, k=0;
12493 while (e ) { e = e.previousSibling; ++k;}
12498 this.rendered = false;
12500 this.render(this.parent().getChildContainer(true), k);
12511 if(Roo.isTouch && this.mobileTouchView){
12512 this.initTouchView();
12517 this.initTickableEvents();
12521 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12523 if(this.hiddenName){
12525 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12527 this.hiddenField.dom.value =
12528 this.hiddenValue !== undefined ? this.hiddenValue :
12529 this.value !== undefined ? this.value : '';
12531 // prevent input submission
12532 this.el.dom.removeAttribute('name');
12533 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12538 // this.el.dom.setAttribute('autocomplete', 'off');
12541 var cls = 'x-combo-list';
12543 //this.list = new Roo.Layer({
12544 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12550 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12551 _this.list.setWidth(lw);
12554 this.list.on('mouseover', this.onViewOver, this);
12555 this.list.on('mousemove', this.onViewMove, this);
12557 this.list.on('scroll', this.onViewScroll, this);
12560 this.list.swallowEvent('mousewheel');
12561 this.assetHeight = 0;
12564 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12565 this.assetHeight += this.header.getHeight();
12568 this.innerList = this.list.createChild({cls:cls+'-inner'});
12569 this.innerList.on('mouseover', this.onViewOver, this);
12570 this.innerList.on('mousemove', this.onViewMove, this);
12571 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12573 if(this.allowBlank && !this.pageSize && !this.disableClear){
12574 this.footer = this.list.createChild({cls:cls+'-ft'});
12575 this.pageTb = new Roo.Toolbar(this.footer);
12579 this.footer = this.list.createChild({cls:cls+'-ft'});
12580 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12581 {pageSize: this.pageSize});
12585 if (this.pageTb && this.allowBlank && !this.disableClear) {
12587 this.pageTb.add(new Roo.Toolbar.Fill(), {
12588 cls: 'x-btn-icon x-btn-clear',
12590 handler: function()
12593 _this.clearValue();
12594 _this.onSelect(false, -1);
12599 this.assetHeight += this.footer.getHeight();
12604 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12607 this.view = new Roo.View(this.list, this.tpl, {
12608 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12610 //this.view.wrapEl.setDisplayed(false);
12611 this.view.on('click', this.onViewClick, this);
12615 this.store.on('beforeload', this.onBeforeLoad, this);
12616 this.store.on('load', this.onLoad, this);
12617 this.store.on('loadexception', this.onLoadException, this);
12619 if(this.resizable){
12620 this.resizer = new Roo.Resizable(this.list, {
12621 pinned:true, handles:'se'
12623 this.resizer.on('resize', function(r, w, h){
12624 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12625 this.listWidth = w;
12626 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12627 this.restrictHeight();
12629 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12632 if(!this.editable){
12633 this.editable = true;
12634 this.setEditable(false);
12639 if (typeof(this.events.add.listeners) != 'undefined') {
12641 this.addicon = this.wrap.createChild(
12642 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12644 this.addicon.on('click', function(e) {
12645 this.fireEvent('add', this);
12648 if (typeof(this.events.edit.listeners) != 'undefined') {
12650 this.editicon = this.wrap.createChild(
12651 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12652 if (this.addicon) {
12653 this.editicon.setStyle('margin-left', '40px');
12655 this.editicon.on('click', function(e) {
12657 // we fire even if inothing is selected..
12658 this.fireEvent('edit', this, this.lastData );
12664 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12665 "up" : function(e){
12666 this.inKeyMode = true;
12670 "down" : function(e){
12671 if(!this.isExpanded()){
12672 this.onTriggerClick();
12674 this.inKeyMode = true;
12679 "enter" : function(e){
12680 // this.onViewClick();
12684 if(this.fireEvent("specialkey", this, e)){
12685 this.onViewClick(false);
12691 "esc" : function(e){
12695 "tab" : function(e){
12698 if(this.fireEvent("specialkey", this, e)){
12699 this.onViewClick(false);
12707 doRelay : function(foo, bar, hname){
12708 if(hname == 'down' || this.scope.isExpanded()){
12709 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12718 this.queryDelay = Math.max(this.queryDelay || 10,
12719 this.mode == 'local' ? 10 : 250);
12722 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12724 if(this.typeAhead){
12725 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12727 if(this.editable !== false){
12728 this.inputEl().on("keyup", this.onKeyUp, this);
12730 if(this.forceSelection){
12731 this.inputEl().on('blur', this.doForce, this);
12735 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12736 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12740 initTickableEvents: function()
12744 if(this.hiddenName){
12746 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12748 this.hiddenField.dom.value =
12749 this.hiddenValue !== undefined ? this.hiddenValue :
12750 this.value !== undefined ? this.value : '';
12752 // prevent input submission
12753 this.el.dom.removeAttribute('name');
12754 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12759 // this.list = this.el.select('ul.dropdown-menu',true).first();
12761 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12762 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12763 if(this.triggerList){
12764 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12767 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12768 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12770 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12771 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12773 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12774 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12776 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12777 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12778 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12781 this.cancelBtn.hide();
12786 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12787 _this.list.setWidth(lw);
12790 this.list.on('mouseover', this.onViewOver, this);
12791 this.list.on('mousemove', this.onViewMove, this);
12793 this.list.on('scroll', this.onViewScroll, this);
12796 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>';
12799 this.view = new Roo.View(this.list, this.tpl, {
12800 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12803 //this.view.wrapEl.setDisplayed(false);
12804 this.view.on('click', this.onViewClick, this);
12808 this.store.on('beforeload', this.onBeforeLoad, this);
12809 this.store.on('load', this.onLoad, this);
12810 this.store.on('loadexception', this.onLoadException, this);
12813 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12814 "up" : function(e){
12815 this.inKeyMode = true;
12819 "down" : function(e){
12820 this.inKeyMode = true;
12824 "enter" : function(e){
12825 if(this.fireEvent("specialkey", this, e)){
12826 this.onViewClick(false);
12832 "esc" : function(e){
12833 this.onTickableFooterButtonClick(e, false, false);
12836 "tab" : function(e){
12837 this.fireEvent("specialkey", this, e);
12839 this.onTickableFooterButtonClick(e, false, false);
12846 doRelay : function(e, fn, key){
12847 if(this.scope.isExpanded()){
12848 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12857 this.queryDelay = Math.max(this.queryDelay || 10,
12858 this.mode == 'local' ? 10 : 250);
12861 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12863 if(this.typeAhead){
12864 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12867 if(this.editable !== false){
12868 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12873 onDestroy : function(){
12875 this.view.setStore(null);
12876 this.view.el.removeAllListeners();
12877 this.view.el.remove();
12878 this.view.purgeListeners();
12881 this.list.dom.innerHTML = '';
12885 this.store.un('beforeload', this.onBeforeLoad, this);
12886 this.store.un('load', this.onLoad, this);
12887 this.store.un('loadexception', this.onLoadException, this);
12889 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12893 fireKey : function(e){
12894 if(e.isNavKeyPress() && !this.list.isVisible()){
12895 this.fireEvent("specialkey", this, e);
12900 onResize: function(w, h){
12901 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12903 // if(typeof w != 'number'){
12904 // // we do not handle it!?!?
12907 // var tw = this.trigger.getWidth();
12908 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12909 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12911 // this.inputEl().setWidth( this.adjustWidth('input', x));
12913 // //this.trigger.setStyle('left', x+'px');
12915 // if(this.list && this.listWidth === undefined){
12916 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12917 // this.list.setWidth(lw);
12918 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12926 * Allow or prevent the user from directly editing the field text. If false is passed,
12927 * the user will only be able to select from the items defined in the dropdown list. This method
12928 * is the runtime equivalent of setting the 'editable' config option at config time.
12929 * @param {Boolean} value True to allow the user to directly edit the field text
12931 setEditable : function(value){
12932 if(value == this.editable){
12935 this.editable = value;
12937 this.inputEl().dom.setAttribute('readOnly', true);
12938 this.inputEl().on('mousedown', this.onTriggerClick, this);
12939 this.inputEl().addClass('x-combo-noedit');
12941 this.inputEl().dom.setAttribute('readOnly', false);
12942 this.inputEl().un('mousedown', this.onTriggerClick, this);
12943 this.inputEl().removeClass('x-combo-noedit');
12949 onBeforeLoad : function(combo,opts){
12950 if(!this.hasFocus){
12954 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12956 this.restrictHeight();
12957 this.selectedIndex = -1;
12961 onLoad : function(){
12963 this.hasQuery = false;
12965 if(!this.hasFocus){
12969 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12970 this.loading.hide();
12973 if(this.store.getCount() > 0){
12975 this.restrictHeight();
12976 if(this.lastQuery == this.allQuery){
12977 if(this.editable && !this.tickable){
12978 this.inputEl().dom.select();
12982 !this.selectByValue(this.value, true) &&
12985 !this.store.lastOptions ||
12986 typeof(this.store.lastOptions.add) == 'undefined' ||
12987 this.store.lastOptions.add != true
12990 this.select(0, true);
12993 if(this.autoFocus){
12996 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12997 this.taTask.delay(this.typeAheadDelay);
13001 this.onEmptyResults();
13007 onLoadException : function()
13009 this.hasQuery = false;
13011 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13012 this.loading.hide();
13015 if(this.tickable && this.editable){
13020 // only causes errors at present
13021 //Roo.log(this.store.reader.jsonData);
13022 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13024 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13030 onTypeAhead : function(){
13031 if(this.store.getCount() > 0){
13032 var r = this.store.getAt(0);
13033 var newValue = r.data[this.displayField];
13034 var len = newValue.length;
13035 var selStart = this.getRawValue().length;
13037 if(selStart != len){
13038 this.setRawValue(newValue);
13039 this.selectText(selStart, newValue.length);
13045 onSelect : function(record, index){
13047 if(this.fireEvent('beforeselect', this, record, index) !== false){
13049 this.setFromData(index > -1 ? record.data : false);
13052 this.fireEvent('select', this, record, index);
13057 * Returns the currently selected field value or empty string if no value is set.
13058 * @return {String} value The selected value
13060 getValue : function(){
13063 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13066 if(this.valueField){
13067 return typeof this.value != 'undefined' ? this.value : '';
13069 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13074 * Clears any text/value currently set in the field
13076 clearValue : function(){
13077 if(this.hiddenField){
13078 this.hiddenField.dom.value = '';
13081 this.setRawValue('');
13082 this.lastSelectionText = '';
13083 this.lastData = false;
13085 var close = this.closeTriggerEl();
13096 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13097 * will be displayed in the field. If the value does not match the data value of an existing item,
13098 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13099 * Otherwise the field will be blank (although the value will still be set).
13100 * @param {String} value The value to match
13102 setValue : function(v){
13109 if(this.valueField){
13110 var r = this.findRecord(this.valueField, v);
13112 text = r.data[this.displayField];
13113 }else if(this.valueNotFoundText !== undefined){
13114 text = this.valueNotFoundText;
13117 this.lastSelectionText = text;
13118 if(this.hiddenField){
13119 this.hiddenField.dom.value = v;
13121 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13124 var close = this.closeTriggerEl();
13127 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13133 * @property {Object} the last set data for the element
13138 * Sets the value of the field based on a object which is related to the record format for the store.
13139 * @param {Object} value the value to set as. or false on reset?
13141 setFromData : function(o){
13148 var dv = ''; // display value
13149 var vv = ''; // value value..
13151 if (this.displayField) {
13152 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13154 // this is an error condition!!!
13155 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13158 if(this.valueField){
13159 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13162 var close = this.closeTriggerEl();
13165 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13168 if(this.hiddenField){
13169 this.hiddenField.dom.value = vv;
13171 this.lastSelectionText = dv;
13172 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13176 // no hidden field.. - we store the value in 'value', but still display
13177 // display field!!!!
13178 this.lastSelectionText = dv;
13179 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13186 reset : function(){
13187 // overridden so that last data is reset..
13194 this.setValue(this.originalValue);
13195 //this.clearInvalid();
13196 this.lastData = false;
13198 this.view.clearSelections();
13204 findRecord : function(prop, value){
13206 if(this.store.getCount() > 0){
13207 this.store.each(function(r){
13208 if(r.data[prop] == value){
13218 getName: function()
13220 // returns hidden if it's set..
13221 if (!this.rendered) {return ''};
13222 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13226 onViewMove : function(e, t){
13227 this.inKeyMode = false;
13231 onViewOver : function(e, t){
13232 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13235 var item = this.view.findItemFromChild(t);
13238 var index = this.view.indexOf(item);
13239 this.select(index, false);
13244 onViewClick : function(view, doFocus, el, e)
13246 var index = this.view.getSelectedIndexes()[0];
13248 var r = this.store.getAt(index);
13252 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13259 Roo.each(this.tickItems, function(v,k){
13261 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13263 _this.tickItems.splice(k, 1);
13265 if(typeof(e) == 'undefined' && view == false){
13266 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13278 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13279 this.tickItems.push(r.data);
13282 if(typeof(e) == 'undefined' && view == false){
13283 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13290 this.onSelect(r, index);
13292 if(doFocus !== false && !this.blockFocus){
13293 this.inputEl().focus();
13298 restrictHeight : function(){
13299 //this.innerList.dom.style.height = '';
13300 //var inner = this.innerList.dom;
13301 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13302 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13303 //this.list.beginUpdate();
13304 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13305 this.list.alignTo(this.inputEl(), this.listAlign);
13306 this.list.alignTo(this.inputEl(), this.listAlign);
13307 //this.list.endUpdate();
13311 onEmptyResults : function(){
13313 if(this.tickable && this.editable){
13314 this.restrictHeight();
13322 * Returns true if the dropdown list is expanded, else false.
13324 isExpanded : function(){
13325 return this.list.isVisible();
13329 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13330 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13331 * @param {String} value The data value of the item to select
13332 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13333 * selected item if it is not currently in view (defaults to true)
13334 * @return {Boolean} True if the value matched an item in the list, else false
13336 selectByValue : function(v, scrollIntoView){
13337 if(v !== undefined && v !== null){
13338 var r = this.findRecord(this.valueField || this.displayField, v);
13340 this.select(this.store.indexOf(r), scrollIntoView);
13348 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13349 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13350 * @param {Number} index The zero-based index of the list item to select
13351 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13352 * selected item if it is not currently in view (defaults to true)
13354 select : function(index, scrollIntoView){
13355 this.selectedIndex = index;
13356 this.view.select(index);
13357 if(scrollIntoView !== false){
13358 var el = this.view.getNode(index);
13360 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13363 this.list.scrollChildIntoView(el, false);
13369 selectNext : function(){
13370 var ct = this.store.getCount();
13372 if(this.selectedIndex == -1){
13374 }else if(this.selectedIndex < ct-1){
13375 this.select(this.selectedIndex+1);
13381 selectPrev : function(){
13382 var ct = this.store.getCount();
13384 if(this.selectedIndex == -1){
13386 }else if(this.selectedIndex != 0){
13387 this.select(this.selectedIndex-1);
13393 onKeyUp : function(e){
13394 if(this.editable !== false && !e.isSpecialKey()){
13395 this.lastKey = e.getKey();
13396 this.dqTask.delay(this.queryDelay);
13401 validateBlur : function(){
13402 return !this.list || !this.list.isVisible();
13406 initQuery : function(){
13408 var v = this.getRawValue();
13410 if(this.tickable && this.editable){
13411 v = this.tickableInputEl().getValue();
13418 doForce : function(){
13419 if(this.inputEl().dom.value.length > 0){
13420 this.inputEl().dom.value =
13421 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13427 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13428 * query allowing the query action to be canceled if needed.
13429 * @param {String} query The SQL query to execute
13430 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13431 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13432 * saved in the current store (defaults to false)
13434 doQuery : function(q, forceAll){
13436 if(q === undefined || q === null){
13441 forceAll: forceAll,
13445 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13450 forceAll = qe.forceAll;
13451 if(forceAll === true || (q.length >= this.minChars)){
13453 this.hasQuery = true;
13455 if(this.lastQuery != q || this.alwaysQuery){
13456 this.lastQuery = q;
13457 if(this.mode == 'local'){
13458 this.selectedIndex = -1;
13460 this.store.clearFilter();
13463 if(this.specialFilter){
13464 this.fireEvent('specialfilter', this);
13469 this.store.filter(this.displayField, q);
13472 this.store.fireEvent("datachanged", this.store);
13479 this.store.baseParams[this.queryParam] = q;
13481 var options = {params : this.getParams(q)};
13484 options.add = true;
13485 options.params.start = this.page * this.pageSize;
13488 this.store.load(options);
13491 * this code will make the page width larger, at the beginning, the list not align correctly,
13492 * we should expand the list on onLoad
13493 * so command out it
13498 this.selectedIndex = -1;
13503 this.loadNext = false;
13507 getParams : function(q){
13509 //p[this.queryParam] = q;
13513 p.limit = this.pageSize;
13519 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13521 collapse : function(){
13522 if(!this.isExpanded()){
13529 this.hasFocus = false;
13531 this.cancelBtn.hide();
13532 this.trigger.show();
13535 this.tickableInputEl().dom.value = '';
13536 this.tickableInputEl().blur();
13541 Roo.get(document).un('mousedown', this.collapseIf, this);
13542 Roo.get(document).un('mousewheel', this.collapseIf, this);
13543 if (!this.editable) {
13544 Roo.get(document).un('keydown', this.listKeyPress, this);
13546 this.fireEvent('collapse', this);
13552 collapseIf : function(e){
13553 var in_combo = e.within(this.el);
13554 var in_list = e.within(this.list);
13555 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13557 if (in_combo || in_list || is_list) {
13558 //e.stopPropagation();
13563 this.onTickableFooterButtonClick(e, false, false);
13571 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13573 expand : function(){
13575 if(this.isExpanded() || !this.hasFocus){
13579 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13580 this.list.setWidth(lw);
13587 this.restrictHeight();
13591 this.tickItems = Roo.apply([], this.item);
13594 this.cancelBtn.show();
13595 this.trigger.hide();
13598 this.tickableInputEl().focus();
13603 Roo.get(document).on('mousedown', this.collapseIf, this);
13604 Roo.get(document).on('mousewheel', this.collapseIf, this);
13605 if (!this.editable) {
13606 Roo.get(document).on('keydown', this.listKeyPress, this);
13609 this.fireEvent('expand', this);
13613 // Implements the default empty TriggerField.onTriggerClick function
13614 onTriggerClick : function(e)
13616 Roo.log('trigger click');
13618 if(this.disabled || !this.triggerList){
13623 this.loadNext = false;
13625 if(this.isExpanded()){
13627 if (!this.blockFocus) {
13628 this.inputEl().focus();
13632 this.hasFocus = true;
13633 if(this.triggerAction == 'all') {
13634 this.doQuery(this.allQuery, true);
13636 this.doQuery(this.getRawValue());
13638 if (!this.blockFocus) {
13639 this.inputEl().focus();
13644 onTickableTriggerClick : function(e)
13651 this.loadNext = false;
13652 this.hasFocus = true;
13654 if(this.triggerAction == 'all') {
13655 this.doQuery(this.allQuery, true);
13657 this.doQuery(this.getRawValue());
13661 onSearchFieldClick : function(e)
13663 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13664 this.onTickableFooterButtonClick(e, false, false);
13668 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13673 this.loadNext = false;
13674 this.hasFocus = true;
13676 if(this.triggerAction == 'all') {
13677 this.doQuery(this.allQuery, true);
13679 this.doQuery(this.getRawValue());
13683 listKeyPress : function(e)
13685 //Roo.log('listkeypress');
13686 // scroll to first matching element based on key pres..
13687 if (e.isSpecialKey()) {
13690 var k = String.fromCharCode(e.getKey()).toUpperCase();
13693 var csel = this.view.getSelectedNodes();
13694 var cselitem = false;
13696 var ix = this.view.indexOf(csel[0]);
13697 cselitem = this.store.getAt(ix);
13698 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13704 this.store.each(function(v) {
13706 // start at existing selection.
13707 if (cselitem.id == v.id) {
13713 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13714 match = this.store.indexOf(v);
13720 if (match === false) {
13721 return true; // no more action?
13724 this.view.select(match);
13725 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13726 sn.scrollIntoView(sn.dom.parentNode, false);
13729 onViewScroll : function(e, t){
13731 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){
13735 this.hasQuery = true;
13737 this.loading = this.list.select('.loading', true).first();
13739 if(this.loading === null){
13740 this.list.createChild({
13742 cls: 'loading roo-select2-more-results roo-select2-active',
13743 html: 'Loading more results...'
13746 this.loading = this.list.select('.loading', true).first();
13748 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13750 this.loading.hide();
13753 this.loading.show();
13758 this.loadNext = true;
13760 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13765 addItem : function(o)
13767 var dv = ''; // display value
13769 if (this.displayField) {
13770 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13772 // this is an error condition!!!
13773 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13780 var choice = this.choices.createChild({
13782 cls: 'roo-select2-search-choice',
13791 cls: 'roo-select2-search-choice-close',
13796 }, this.searchField);
13798 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13800 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13808 this.inputEl().dom.value = '';
13813 onRemoveItem : function(e, _self, o)
13815 e.preventDefault();
13817 this.lastItem = Roo.apply([], this.item);
13819 var index = this.item.indexOf(o.data) * 1;
13822 Roo.log('not this item?!');
13826 this.item.splice(index, 1);
13831 this.fireEvent('remove', this, e);
13837 syncValue : function()
13839 if(!this.item.length){
13846 Roo.each(this.item, function(i){
13847 if(_this.valueField){
13848 value.push(i[_this.valueField]);
13855 this.value = value.join(',');
13857 if(this.hiddenField){
13858 this.hiddenField.dom.value = this.value;
13861 this.store.fireEvent("datachanged", this.store);
13866 clearItem : function()
13868 if(!this.multiple){
13874 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13882 if(this.tickable && !Roo.isTouch){
13883 this.view.refresh();
13887 inputEl: function ()
13889 if(Roo.isTouch && this.mobileTouchView){
13890 return this.el.select('input.form-control',true).first();
13894 return this.searchField;
13897 return this.el.select('input.form-control',true).first();
13901 onTickableFooterButtonClick : function(e, btn, el)
13903 e.preventDefault();
13905 this.lastItem = Roo.apply([], this.item);
13907 if(btn && btn.name == 'cancel'){
13908 this.tickItems = Roo.apply([], this.item);
13917 Roo.each(this.tickItems, function(o){
13925 validate : function()
13927 var v = this.getRawValue();
13930 v = this.getValue();
13933 if(this.disabled || this.allowBlank || v.length){
13938 this.markInvalid();
13942 tickableInputEl : function()
13944 if(!this.tickable || !this.editable){
13945 return this.inputEl();
13948 return this.inputEl().select('.roo-select2-search-field-input', true).first();
13952 getAutoCreateTouchView : function()
13957 cls: 'form-group' //input-group
13963 type : this.inputType,
13964 cls : 'form-control x-combo-noedit',
13965 autocomplete: 'new-password',
13966 placeholder : this.placeholder || '',
13971 input.name = this.name;
13975 input.cls += ' input-' + this.size;
13978 if (this.disabled) {
13979 input.disabled = true;
13990 inputblock.cls += ' input-group';
13992 inputblock.cn.unshift({
13994 cls : 'input-group-addon',
13999 if(this.removable && !this.multiple){
14000 inputblock.cls += ' roo-removable';
14002 inputblock.cn.push({
14005 cls : 'roo-combo-removable-btn close'
14009 if(this.hasFeedback && !this.allowBlank){
14011 inputblock.cls += ' has-feedback';
14013 inputblock.cn.push({
14015 cls: 'glyphicon form-control-feedback'
14022 inputblock.cls += (this.before) ? '' : ' input-group';
14024 inputblock.cn.push({
14026 cls : 'input-group-addon',
14037 cls: 'form-hidden-field'
14051 cls: 'form-hidden-field'
14055 cls: 'roo-select2-choices',
14059 cls: 'roo-select2-search-field',
14072 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14078 if(!this.multiple && this.showToggleBtn){
14085 if (this.caret != false) {
14088 cls: 'fa fa-' + this.caret
14095 cls : 'input-group-addon btn dropdown-toggle',
14100 cls: 'combobox-clear',
14114 combobox.cls += ' roo-select2-container-multi';
14117 var align = this.labelAlign || this.parentLabelAlign();
14121 if(this.fieldLabel.length && this.labelWidth){
14123 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14124 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14129 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14130 tooltip : 'This field is required'
14134 cls : 'control-label ' + lw,
14135 html : this.fieldLabel
14146 if(this.indicatorpos == 'right'){
14150 cls : 'control-label ' + lw,
14151 html : this.fieldLabel
14156 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14157 tooltip : 'This field is required'
14169 var settings = this;
14171 ['xs','sm','md','lg'].map(function(size){
14172 if (settings[size]) {
14173 cfg.cls += ' col-' + size + '-' + settings[size];
14180 initTouchView : function()
14182 this.renderTouchView();
14184 this.touchViewEl.on('scroll', function(){
14185 this.el.dom.scrollTop = 0;
14188 this.originalValue = this.getValue();
14190 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14192 this.inputEl().on("click", this.showTouchView, this);
14193 this.triggerEl.on("click", this.showTouchView, this);
14195 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14196 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14198 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14200 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14201 this.store.on('load', this.onTouchViewLoad, this);
14202 this.store.on('loadexception', this.onTouchViewLoadException, this);
14204 if(this.hiddenName){
14206 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14208 this.hiddenField.dom.value =
14209 this.hiddenValue !== undefined ? this.hiddenValue :
14210 this.value !== undefined ? this.value : '';
14212 this.el.dom.removeAttribute('name');
14213 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14217 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14218 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14221 if(this.removable && !this.multiple){
14222 var close = this.closeTriggerEl();
14224 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14225 close.on('click', this.removeBtnClick, this, close);
14229 * fix the bug in Safari iOS8
14231 this.inputEl().on("focus", function(e){
14232 document.activeElement.blur();
14240 renderTouchView : function()
14242 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14243 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14245 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14246 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14248 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14249 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14250 this.touchViewBodyEl.setStyle('overflow', 'auto');
14252 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14253 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14255 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14256 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14260 showTouchView : function()
14266 this.touchViewHeaderEl.hide();
14268 if(this.modalTitle.length){
14269 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14270 this.touchViewHeaderEl.show();
14273 this.touchViewEl.show();
14275 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14276 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14277 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14279 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14281 if(this.modalTitle.length){
14282 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14285 this.touchViewBodyEl.setHeight(bodyHeight);
14289 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14291 this.touchViewEl.addClass('in');
14294 this.doTouchViewQuery();
14298 hideTouchView : function()
14300 this.touchViewEl.removeClass('in');
14304 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14306 this.touchViewEl.setStyle('display', 'none');
14311 setTouchViewValue : function()
14318 Roo.each(this.tickItems, function(o){
14323 this.hideTouchView();
14326 doTouchViewQuery : function()
14335 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14339 if(!this.alwaysQuery || this.mode == 'local'){
14340 this.onTouchViewLoad();
14347 onTouchViewBeforeLoad : function(combo,opts)
14353 onTouchViewLoad : function()
14355 if(this.store.getCount() < 1){
14356 this.onTouchViewEmptyResults();
14360 this.clearTouchView();
14362 var rawValue = this.getRawValue();
14364 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14366 this.tickItems = [];
14368 this.store.data.each(function(d, rowIndex){
14369 var row = this.touchViewListGroup.createChild(template);
14371 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14372 row.addClass(d.data.cls);
14375 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14378 html : d.data[this.displayField]
14381 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14382 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14385 row.removeClass('selected');
14386 if(!this.multiple && this.valueField &&
14387 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14390 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14391 row.addClass('selected');
14394 if(this.multiple && this.valueField &&
14395 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14399 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14400 this.tickItems.push(d.data);
14403 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14407 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14409 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14411 if(this.modalTitle.length){
14412 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14415 var listHeight = this.touchViewListGroup.getHeight();
14419 if(firstChecked && listHeight > bodyHeight){
14420 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14425 onTouchViewLoadException : function()
14427 this.hideTouchView();
14430 onTouchViewEmptyResults : function()
14432 this.clearTouchView();
14434 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14436 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14440 clearTouchView : function()
14442 this.touchViewListGroup.dom.innerHTML = '';
14445 onTouchViewClick : function(e, el, o)
14447 e.preventDefault();
14450 var rowIndex = o.rowIndex;
14452 var r = this.store.getAt(rowIndex);
14454 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14456 if(!this.multiple){
14457 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14458 c.dom.removeAttribute('checked');
14461 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14463 this.setFromData(r.data);
14465 var close = this.closeTriggerEl();
14471 this.hideTouchView();
14473 this.fireEvent('select', this, r, rowIndex);
14478 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14479 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14480 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14484 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14485 this.addItem(r.data);
14486 this.tickItems.push(r.data);
14492 * @cfg {Boolean} grow
14496 * @cfg {Number} growMin
14500 * @cfg {Number} growMax
14509 Roo.apply(Roo.bootstrap.ComboBox, {
14513 cls: 'modal-header',
14535 cls: 'list-group-item',
14539 cls: 'roo-combobox-list-group-item-value'
14543 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14557 listItemCheckbox : {
14559 cls: 'list-group-item',
14563 cls: 'roo-combobox-list-group-item-value'
14567 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14583 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14588 cls: 'modal-footer',
14596 cls: 'col-xs-6 text-left',
14599 cls: 'btn btn-danger roo-touch-view-cancel',
14605 cls: 'col-xs-6 text-right',
14608 cls: 'btn btn-success roo-touch-view-ok',
14619 Roo.apply(Roo.bootstrap.ComboBox, {
14621 touchViewTemplate : {
14623 cls: 'modal fade roo-combobox-touch-view',
14627 cls: 'modal-dialog',
14628 style : 'position:fixed', // we have to fix position....
14632 cls: 'modal-content',
14634 Roo.bootstrap.ComboBox.header,
14635 Roo.bootstrap.ComboBox.body,
14636 Roo.bootstrap.ComboBox.footer
14645 * Ext JS Library 1.1.1
14646 * Copyright(c) 2006-2007, Ext JS, LLC.
14648 * Originally Released Under LGPL - original licence link has changed is not relivant.
14651 * <script type="text/javascript">
14656 * @extends Roo.util.Observable
14657 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14658 * This class also supports single and multi selection modes. <br>
14659 * Create a data model bound view:
14661 var store = new Roo.data.Store(...);
14663 var view = new Roo.View({
14665 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14667 singleSelect: true,
14668 selectedClass: "ydataview-selected",
14672 // listen for node click?
14673 view.on("click", function(vw, index, node, e){
14674 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14678 dataModel.load("foobar.xml");
14680 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14682 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14683 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14685 * Note: old style constructor is still suported (container, template, config)
14688 * Create a new View
14689 * @param {Object} config The config object
14692 Roo.View = function(config, depreciated_tpl, depreciated_config){
14694 this.parent = false;
14696 if (typeof(depreciated_tpl) == 'undefined') {
14697 // new way.. - universal constructor.
14698 Roo.apply(this, config);
14699 this.el = Roo.get(this.el);
14702 this.el = Roo.get(config);
14703 this.tpl = depreciated_tpl;
14704 Roo.apply(this, depreciated_config);
14706 this.wrapEl = this.el.wrap().wrap();
14707 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14710 if(typeof(this.tpl) == "string"){
14711 this.tpl = new Roo.Template(this.tpl);
14713 // support xtype ctors..
14714 this.tpl = new Roo.factory(this.tpl, Roo);
14718 this.tpl.compile();
14723 * @event beforeclick
14724 * Fires before a click is processed. Returns false to cancel the default action.
14725 * @param {Roo.View} this
14726 * @param {Number} index The index of the target node
14727 * @param {HTMLElement} node The target node
14728 * @param {Roo.EventObject} e The raw event object
14730 "beforeclick" : true,
14733 * Fires when a template node is clicked.
14734 * @param {Roo.View} this
14735 * @param {Number} index The index of the target node
14736 * @param {HTMLElement} node The target node
14737 * @param {Roo.EventObject} e The raw event object
14742 * Fires when a template node is double clicked.
14743 * @param {Roo.View} this
14744 * @param {Number} index The index of the target node
14745 * @param {HTMLElement} node The target node
14746 * @param {Roo.EventObject} e The raw event object
14750 * @event contextmenu
14751 * Fires when a template node is right clicked.
14752 * @param {Roo.View} this
14753 * @param {Number} index The index of the target node
14754 * @param {HTMLElement} node The target node
14755 * @param {Roo.EventObject} e The raw event object
14757 "contextmenu" : true,
14759 * @event selectionchange
14760 * Fires when the selected nodes change.
14761 * @param {Roo.View} this
14762 * @param {Array} selections Array of the selected nodes
14764 "selectionchange" : true,
14767 * @event beforeselect
14768 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14769 * @param {Roo.View} this
14770 * @param {HTMLElement} node The node to be selected
14771 * @param {Array} selections Array of currently selected nodes
14773 "beforeselect" : true,
14775 * @event preparedata
14776 * Fires on every row to render, to allow you to change the data.
14777 * @param {Roo.View} this
14778 * @param {Object} data to be rendered (change this)
14780 "preparedata" : true
14788 "click": this.onClick,
14789 "dblclick": this.onDblClick,
14790 "contextmenu": this.onContextMenu,
14794 this.selections = [];
14796 this.cmp = new Roo.CompositeElementLite([]);
14798 this.store = Roo.factory(this.store, Roo.data);
14799 this.setStore(this.store, true);
14802 if ( this.footer && this.footer.xtype) {
14804 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14806 this.footer.dataSource = this.store;
14807 this.footer.container = fctr;
14808 this.footer = Roo.factory(this.footer, Roo);
14809 fctr.insertFirst(this.el);
14811 // this is a bit insane - as the paging toolbar seems to detach the el..
14812 // dom.parentNode.parentNode.parentNode
14813 // they get detached?
14817 Roo.View.superclass.constructor.call(this);
14822 Roo.extend(Roo.View, Roo.util.Observable, {
14825 * @cfg {Roo.data.Store} store Data store to load data from.
14830 * @cfg {String|Roo.Element} el The container element.
14835 * @cfg {String|Roo.Template} tpl The template used by this View
14839 * @cfg {String} dataName the named area of the template to use as the data area
14840 * Works with domtemplates roo-name="name"
14844 * @cfg {String} selectedClass The css class to add to selected nodes
14846 selectedClass : "x-view-selected",
14848 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14853 * @cfg {String} text to display on mask (default Loading)
14857 * @cfg {Boolean} multiSelect Allow multiple selection
14859 multiSelect : false,
14861 * @cfg {Boolean} singleSelect Allow single selection
14863 singleSelect: false,
14866 * @cfg {Boolean} toggleSelect - selecting
14868 toggleSelect : false,
14871 * @cfg {Boolean} tickable - selecting
14876 * Returns the element this view is bound to.
14877 * @return {Roo.Element}
14879 getEl : function(){
14880 return this.wrapEl;
14886 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14888 refresh : function(){
14889 //Roo.log('refresh');
14892 // if we are using something like 'domtemplate', then
14893 // the what gets used is:
14894 // t.applySubtemplate(NAME, data, wrapping data..)
14895 // the outer template then get' applied with
14896 // the store 'extra data'
14897 // and the body get's added to the
14898 // roo-name="data" node?
14899 // <span class='roo-tpl-{name}'></span> ?????
14903 this.clearSelections();
14904 this.el.update("");
14906 var records = this.store.getRange();
14907 if(records.length < 1) {
14909 // is this valid?? = should it render a template??
14911 this.el.update(this.emptyText);
14915 if (this.dataName) {
14916 this.el.update(t.apply(this.store.meta)); //????
14917 el = this.el.child('.roo-tpl-' + this.dataName);
14920 for(var i = 0, len = records.length; i < len; i++){
14921 var data = this.prepareData(records[i].data, i, records[i]);
14922 this.fireEvent("preparedata", this, data, i, records[i]);
14924 var d = Roo.apply({}, data);
14927 Roo.apply(d, {'roo-id' : Roo.id()});
14931 Roo.each(this.parent.item, function(item){
14932 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14935 Roo.apply(d, {'roo-data-checked' : 'checked'});
14939 html[html.length] = Roo.util.Format.trim(
14941 t.applySubtemplate(this.dataName, d, this.store.meta) :
14948 el.update(html.join(""));
14949 this.nodes = el.dom.childNodes;
14950 this.updateIndexes(0);
14955 * Function to override to reformat the data that is sent to
14956 * the template for each node.
14957 * DEPRICATED - use the preparedata event handler.
14958 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14959 * a JSON object for an UpdateManager bound view).
14961 prepareData : function(data, index, record)
14963 this.fireEvent("preparedata", this, data, index, record);
14967 onUpdate : function(ds, record){
14968 // Roo.log('on update');
14969 this.clearSelections();
14970 var index = this.store.indexOf(record);
14971 var n = this.nodes[index];
14972 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14973 n.parentNode.removeChild(n);
14974 this.updateIndexes(index, index);
14980 onAdd : function(ds, records, index)
14982 //Roo.log(['on Add', ds, records, index] );
14983 this.clearSelections();
14984 if(this.nodes.length == 0){
14988 var n = this.nodes[index];
14989 for(var i = 0, len = records.length; i < len; i++){
14990 var d = this.prepareData(records[i].data, i, records[i]);
14992 this.tpl.insertBefore(n, d);
14995 this.tpl.append(this.el, d);
14998 this.updateIndexes(index);
15001 onRemove : function(ds, record, index){
15002 // Roo.log('onRemove');
15003 this.clearSelections();
15004 var el = this.dataName ?
15005 this.el.child('.roo-tpl-' + this.dataName) :
15008 el.dom.removeChild(this.nodes[index]);
15009 this.updateIndexes(index);
15013 * Refresh an individual node.
15014 * @param {Number} index
15016 refreshNode : function(index){
15017 this.onUpdate(this.store, this.store.getAt(index));
15020 updateIndexes : function(startIndex, endIndex){
15021 var ns = this.nodes;
15022 startIndex = startIndex || 0;
15023 endIndex = endIndex || ns.length - 1;
15024 for(var i = startIndex; i <= endIndex; i++){
15025 ns[i].nodeIndex = i;
15030 * Changes the data store this view uses and refresh the view.
15031 * @param {Store} store
15033 setStore : function(store, initial){
15034 if(!initial && this.store){
15035 this.store.un("datachanged", this.refresh);
15036 this.store.un("add", this.onAdd);
15037 this.store.un("remove", this.onRemove);
15038 this.store.un("update", this.onUpdate);
15039 this.store.un("clear", this.refresh);
15040 this.store.un("beforeload", this.onBeforeLoad);
15041 this.store.un("load", this.onLoad);
15042 this.store.un("loadexception", this.onLoad);
15046 store.on("datachanged", this.refresh, this);
15047 store.on("add", this.onAdd, this);
15048 store.on("remove", this.onRemove, this);
15049 store.on("update", this.onUpdate, this);
15050 store.on("clear", this.refresh, this);
15051 store.on("beforeload", this.onBeforeLoad, this);
15052 store.on("load", this.onLoad, this);
15053 store.on("loadexception", this.onLoad, this);
15061 * onbeforeLoad - masks the loading area.
15064 onBeforeLoad : function(store,opts)
15066 //Roo.log('onBeforeLoad');
15068 this.el.update("");
15070 this.el.mask(this.mask ? this.mask : "Loading" );
15072 onLoad : function ()
15079 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15080 * @param {HTMLElement} node
15081 * @return {HTMLElement} The template node
15083 findItemFromChild : function(node){
15084 var el = this.dataName ?
15085 this.el.child('.roo-tpl-' + this.dataName,true) :
15088 if(!node || node.parentNode == el){
15091 var p = node.parentNode;
15092 while(p && p != el){
15093 if(p.parentNode == el){
15102 onClick : function(e){
15103 var item = this.findItemFromChild(e.getTarget());
15105 var index = this.indexOf(item);
15106 if(this.onItemClick(item, index, e) !== false){
15107 this.fireEvent("click", this, index, item, e);
15110 this.clearSelections();
15115 onContextMenu : function(e){
15116 var item = this.findItemFromChild(e.getTarget());
15118 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15123 onDblClick : function(e){
15124 var item = this.findItemFromChild(e.getTarget());
15126 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15130 onItemClick : function(item, index, e)
15132 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15135 if (this.toggleSelect) {
15136 var m = this.isSelected(item) ? 'unselect' : 'select';
15139 _t[m](item, true, false);
15142 if(this.multiSelect || this.singleSelect){
15143 if(this.multiSelect && e.shiftKey && this.lastSelection){
15144 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15146 this.select(item, this.multiSelect && e.ctrlKey);
15147 this.lastSelection = item;
15150 if(!this.tickable){
15151 e.preventDefault();
15159 * Get the number of selected nodes.
15162 getSelectionCount : function(){
15163 return this.selections.length;
15167 * Get the currently selected nodes.
15168 * @return {Array} An array of HTMLElements
15170 getSelectedNodes : function(){
15171 return this.selections;
15175 * Get the indexes of the selected nodes.
15178 getSelectedIndexes : function(){
15179 var indexes = [], s = this.selections;
15180 for(var i = 0, len = s.length; i < len; i++){
15181 indexes.push(s[i].nodeIndex);
15187 * Clear all selections
15188 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15190 clearSelections : function(suppressEvent){
15191 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15192 this.cmp.elements = this.selections;
15193 this.cmp.removeClass(this.selectedClass);
15194 this.selections = [];
15195 if(!suppressEvent){
15196 this.fireEvent("selectionchange", this, this.selections);
15202 * Returns true if the passed node is selected
15203 * @param {HTMLElement/Number} node The node or node index
15204 * @return {Boolean}
15206 isSelected : function(node){
15207 var s = this.selections;
15211 node = this.getNode(node);
15212 return s.indexOf(node) !== -1;
15217 * @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
15218 * @param {Boolean} keepExisting (optional) true to keep existing selections
15219 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15221 select : function(nodeInfo, keepExisting, suppressEvent){
15222 if(nodeInfo instanceof Array){
15224 this.clearSelections(true);
15226 for(var i = 0, len = nodeInfo.length; i < len; i++){
15227 this.select(nodeInfo[i], true, true);
15231 var node = this.getNode(nodeInfo);
15232 if(!node || this.isSelected(node)){
15233 return; // already selected.
15236 this.clearSelections(true);
15239 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15240 Roo.fly(node).addClass(this.selectedClass);
15241 this.selections.push(node);
15242 if(!suppressEvent){
15243 this.fireEvent("selectionchange", this, this.selections);
15251 * @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
15252 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15253 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15255 unselect : function(nodeInfo, keepExisting, suppressEvent)
15257 if(nodeInfo instanceof Array){
15258 Roo.each(this.selections, function(s) {
15259 this.unselect(s, nodeInfo);
15263 var node = this.getNode(nodeInfo);
15264 if(!node || !this.isSelected(node)){
15265 //Roo.log("not selected");
15266 return; // not selected.
15270 Roo.each(this.selections, function(s) {
15272 Roo.fly(node).removeClass(this.selectedClass);
15279 this.selections= ns;
15280 this.fireEvent("selectionchange", this, this.selections);
15284 * Gets a template node.
15285 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15286 * @return {HTMLElement} The node or null if it wasn't found
15288 getNode : function(nodeInfo){
15289 if(typeof nodeInfo == "string"){
15290 return document.getElementById(nodeInfo);
15291 }else if(typeof nodeInfo == "number"){
15292 return this.nodes[nodeInfo];
15298 * Gets a range template nodes.
15299 * @param {Number} startIndex
15300 * @param {Number} endIndex
15301 * @return {Array} An array of nodes
15303 getNodes : function(start, end){
15304 var ns = this.nodes;
15305 start = start || 0;
15306 end = typeof end == "undefined" ? ns.length - 1 : end;
15309 for(var i = start; i <= end; i++){
15313 for(var i = start; i >= end; i--){
15321 * Finds the index of the passed node
15322 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15323 * @return {Number} The index of the node or -1
15325 indexOf : function(node){
15326 node = this.getNode(node);
15327 if(typeof node.nodeIndex == "number"){
15328 return node.nodeIndex;
15330 var ns = this.nodes;
15331 for(var i = 0, len = ns.length; i < len; i++){
15342 * based on jquery fullcalendar
15346 Roo.bootstrap = Roo.bootstrap || {};
15348 * @class Roo.bootstrap.Calendar
15349 * @extends Roo.bootstrap.Component
15350 * Bootstrap Calendar class
15351 * @cfg {Boolean} loadMask (true|false) default false
15352 * @cfg {Object} header generate the user specific header of the calendar, default false
15355 * Create a new Container
15356 * @param {Object} config The config object
15361 Roo.bootstrap.Calendar = function(config){
15362 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15366 * Fires when a date is selected
15367 * @param {DatePicker} this
15368 * @param {Date} date The selected date
15372 * @event monthchange
15373 * Fires when the displayed month changes
15374 * @param {DatePicker} this
15375 * @param {Date} date The selected month
15377 'monthchange': true,
15379 * @event evententer
15380 * Fires when mouse over an event
15381 * @param {Calendar} this
15382 * @param {event} Event
15384 'evententer': true,
15386 * @event eventleave
15387 * Fires when the mouse leaves an
15388 * @param {Calendar} this
15391 'eventleave': true,
15393 * @event eventclick
15394 * Fires when the mouse click an
15395 * @param {Calendar} this
15404 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15407 * @cfg {Number} startDay
15408 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15416 getAutoCreate : function(){
15419 var fc_button = function(name, corner, style, content ) {
15420 return Roo.apply({},{
15422 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15424 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15427 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15438 style : 'width:100%',
15445 cls : 'fc-header-left',
15447 fc_button('prev', 'left', 'arrow', '‹' ),
15448 fc_button('next', 'right', 'arrow', '›' ),
15449 { tag: 'span', cls: 'fc-header-space' },
15450 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15458 cls : 'fc-header-center',
15462 cls: 'fc-header-title',
15465 html : 'month / year'
15473 cls : 'fc-header-right',
15475 /* fc_button('month', 'left', '', 'month' ),
15476 fc_button('week', '', '', 'week' ),
15477 fc_button('day', 'right', '', 'day' )
15489 header = this.header;
15492 var cal_heads = function() {
15494 // fixme - handle this.
15496 for (var i =0; i < Date.dayNames.length; i++) {
15497 var d = Date.dayNames[i];
15500 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15501 html : d.substring(0,3)
15505 ret[0].cls += ' fc-first';
15506 ret[6].cls += ' fc-last';
15509 var cal_cell = function(n) {
15512 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15517 cls: 'fc-day-number',
15521 cls: 'fc-day-content',
15525 style: 'position: relative;' // height: 17px;
15537 var cal_rows = function() {
15540 for (var r = 0; r < 6; r++) {
15547 for (var i =0; i < Date.dayNames.length; i++) {
15548 var d = Date.dayNames[i];
15549 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15552 row.cn[0].cls+=' fc-first';
15553 row.cn[0].cn[0].style = 'min-height:90px';
15554 row.cn[6].cls+=' fc-last';
15558 ret[0].cls += ' fc-first';
15559 ret[4].cls += ' fc-prev-last';
15560 ret[5].cls += ' fc-last';
15567 cls: 'fc-border-separate',
15568 style : 'width:100%',
15576 cls : 'fc-first fc-last',
15594 cls : 'fc-content',
15595 style : "position: relative;",
15598 cls : 'fc-view fc-view-month fc-grid',
15599 style : 'position: relative',
15600 unselectable : 'on',
15603 cls : 'fc-event-container',
15604 style : 'position:absolute;z-index:8;top:0;left:0;'
15622 initEvents : function()
15625 throw "can not find store for calendar";
15631 style: "text-align:center",
15635 style: "background-color:white;width:50%;margin:250 auto",
15639 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15650 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15652 var size = this.el.select('.fc-content', true).first().getSize();
15653 this.maskEl.setSize(size.width, size.height);
15654 this.maskEl.enableDisplayMode("block");
15655 if(!this.loadMask){
15656 this.maskEl.hide();
15659 this.store = Roo.factory(this.store, Roo.data);
15660 this.store.on('load', this.onLoad, this);
15661 this.store.on('beforeload', this.onBeforeLoad, this);
15665 this.cells = this.el.select('.fc-day',true);
15666 //Roo.log(this.cells);
15667 this.textNodes = this.el.query('.fc-day-number');
15668 this.cells.addClassOnOver('fc-state-hover');
15670 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15671 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15672 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15673 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15675 this.on('monthchange', this.onMonthChange, this);
15677 this.update(new Date().clearTime());
15680 resize : function() {
15681 var sz = this.el.getSize();
15683 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15684 this.el.select('.fc-day-content div',true).setHeight(34);
15689 showPrevMonth : function(e){
15690 this.update(this.activeDate.add("mo", -1));
15692 showToday : function(e){
15693 this.update(new Date().clearTime());
15696 showNextMonth : function(e){
15697 this.update(this.activeDate.add("mo", 1));
15701 showPrevYear : function(){
15702 this.update(this.activeDate.add("y", -1));
15706 showNextYear : function(){
15707 this.update(this.activeDate.add("y", 1));
15712 update : function(date)
15714 var vd = this.activeDate;
15715 this.activeDate = date;
15716 // if(vd && this.el){
15717 // var t = date.getTime();
15718 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15719 // Roo.log('using add remove');
15721 // this.fireEvent('monthchange', this, date);
15723 // this.cells.removeClass("fc-state-highlight");
15724 // this.cells.each(function(c){
15725 // if(c.dateValue == t){
15726 // c.addClass("fc-state-highlight");
15727 // setTimeout(function(){
15728 // try{c.dom.firstChild.focus();}catch(e){}
15738 var days = date.getDaysInMonth();
15740 var firstOfMonth = date.getFirstDateOfMonth();
15741 var startingPos = firstOfMonth.getDay()-this.startDay;
15743 if(startingPos < this.startDay){
15747 var pm = date.add(Date.MONTH, -1);
15748 var prevStart = pm.getDaysInMonth()-startingPos;
15750 this.cells = this.el.select('.fc-day',true);
15751 this.textNodes = this.el.query('.fc-day-number');
15752 this.cells.addClassOnOver('fc-state-hover');
15754 var cells = this.cells.elements;
15755 var textEls = this.textNodes;
15757 Roo.each(cells, function(cell){
15758 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15761 days += startingPos;
15763 // convert everything to numbers so it's fast
15764 var day = 86400000;
15765 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15768 //Roo.log(prevStart);
15770 var today = new Date().clearTime().getTime();
15771 var sel = date.clearTime().getTime();
15772 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15773 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15774 var ddMatch = this.disabledDatesRE;
15775 var ddText = this.disabledDatesText;
15776 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15777 var ddaysText = this.disabledDaysText;
15778 var format = this.format;
15780 var setCellClass = function(cal, cell){
15784 //Roo.log('set Cell Class');
15786 var t = d.getTime();
15790 cell.dateValue = t;
15792 cell.className += " fc-today";
15793 cell.className += " fc-state-highlight";
15794 cell.title = cal.todayText;
15797 // disable highlight in other month..
15798 //cell.className += " fc-state-highlight";
15803 cell.className = " fc-state-disabled";
15804 cell.title = cal.minText;
15808 cell.className = " fc-state-disabled";
15809 cell.title = cal.maxText;
15813 if(ddays.indexOf(d.getDay()) != -1){
15814 cell.title = ddaysText;
15815 cell.className = " fc-state-disabled";
15818 if(ddMatch && format){
15819 var fvalue = d.dateFormat(format);
15820 if(ddMatch.test(fvalue)){
15821 cell.title = ddText.replace("%0", fvalue);
15822 cell.className = " fc-state-disabled";
15826 if (!cell.initialClassName) {
15827 cell.initialClassName = cell.dom.className;
15830 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15835 for(; i < startingPos; i++) {
15836 textEls[i].innerHTML = (++prevStart);
15837 d.setDate(d.getDate()+1);
15839 cells[i].className = "fc-past fc-other-month";
15840 setCellClass(this, cells[i]);
15845 for(; i < days; i++){
15846 intDay = i - startingPos + 1;
15847 textEls[i].innerHTML = (intDay);
15848 d.setDate(d.getDate()+1);
15850 cells[i].className = ''; // "x-date-active";
15851 setCellClass(this, cells[i]);
15855 for(; i < 42; i++) {
15856 textEls[i].innerHTML = (++extraDays);
15857 d.setDate(d.getDate()+1);
15859 cells[i].className = "fc-future fc-other-month";
15860 setCellClass(this, cells[i]);
15863 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15865 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15867 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15868 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15870 if(totalRows != 6){
15871 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15872 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15875 this.fireEvent('monthchange', this, date);
15879 if(!this.internalRender){
15880 var main = this.el.dom.firstChild;
15881 var w = main.offsetWidth;
15882 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15883 Roo.fly(main).setWidth(w);
15884 this.internalRender = true;
15885 // opera does not respect the auto grow header center column
15886 // then, after it gets a width opera refuses to recalculate
15887 // without a second pass
15888 if(Roo.isOpera && !this.secondPass){
15889 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15890 this.secondPass = true;
15891 this.update.defer(10, this, [date]);
15898 findCell : function(dt) {
15899 dt = dt.clearTime().getTime();
15901 this.cells.each(function(c){
15902 //Roo.log("check " +c.dateValue + '?=' + dt);
15903 if(c.dateValue == dt){
15913 findCells : function(ev) {
15914 var s = ev.start.clone().clearTime().getTime();
15916 var e= ev.end.clone().clearTime().getTime();
15919 this.cells.each(function(c){
15920 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15922 if(c.dateValue > e){
15925 if(c.dateValue < s){
15934 // findBestRow: function(cells)
15938 // for (var i =0 ; i < cells.length;i++) {
15939 // ret = Math.max(cells[i].rows || 0,ret);
15946 addItem : function(ev)
15948 // look for vertical location slot in
15949 var cells = this.findCells(ev);
15951 // ev.row = this.findBestRow(cells);
15953 // work out the location.
15957 for(var i =0; i < cells.length; i++) {
15959 cells[i].row = cells[0].row;
15962 cells[i].row = cells[i].row + 1;
15972 if (crow.start.getY() == cells[i].getY()) {
15974 crow.end = cells[i];
15991 cells[0].events.push(ev);
15993 this.calevents.push(ev);
15996 clearEvents: function() {
15998 if(!this.calevents){
16002 Roo.each(this.cells.elements, function(c){
16008 Roo.each(this.calevents, function(e) {
16009 Roo.each(e.els, function(el) {
16010 el.un('mouseenter' ,this.onEventEnter, this);
16011 el.un('mouseleave' ,this.onEventLeave, this);
16016 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16022 renderEvents: function()
16026 this.cells.each(function(c) {
16035 if(c.row != c.events.length){
16036 r = 4 - (4 - (c.row - c.events.length));
16039 c.events = ev.slice(0, r);
16040 c.more = ev.slice(r);
16042 if(c.more.length && c.more.length == 1){
16043 c.events.push(c.more.pop());
16046 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16050 this.cells.each(function(c) {
16052 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16055 for (var e = 0; e < c.events.length; e++){
16056 var ev = c.events[e];
16057 var rows = ev.rows;
16059 for(var i = 0; i < rows.length; i++) {
16061 // how many rows should it span..
16064 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16065 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16067 unselectable : "on",
16070 cls: 'fc-event-inner',
16074 // cls: 'fc-event-time',
16075 // html : cells.length > 1 ? '' : ev.time
16079 cls: 'fc-event-title',
16080 html : String.format('{0}', ev.title)
16087 cls: 'ui-resizable-handle ui-resizable-e',
16088 html : '  '
16095 cfg.cls += ' fc-event-start';
16097 if ((i+1) == rows.length) {
16098 cfg.cls += ' fc-event-end';
16101 var ctr = _this.el.select('.fc-event-container',true).first();
16102 var cg = ctr.createChild(cfg);
16104 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16105 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16107 var r = (c.more.length) ? 1 : 0;
16108 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16109 cg.setWidth(ebox.right - sbox.x -2);
16111 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16112 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16113 cg.on('click', _this.onEventClick, _this, ev);
16124 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16125 style : 'position: absolute',
16126 unselectable : "on",
16129 cls: 'fc-event-inner',
16133 cls: 'fc-event-title',
16141 cls: 'ui-resizable-handle ui-resizable-e',
16142 html : '  '
16148 var ctr = _this.el.select('.fc-event-container',true).first();
16149 var cg = ctr.createChild(cfg);
16151 var sbox = c.select('.fc-day-content',true).first().getBox();
16152 var ebox = c.select('.fc-day-content',true).first().getBox();
16154 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16155 cg.setWidth(ebox.right - sbox.x -2);
16157 cg.on('click', _this.onMoreEventClick, _this, c.more);
16167 onEventEnter: function (e, el,event,d) {
16168 this.fireEvent('evententer', this, el, event);
16171 onEventLeave: function (e, el,event,d) {
16172 this.fireEvent('eventleave', this, el, event);
16175 onEventClick: function (e, el,event,d) {
16176 this.fireEvent('eventclick', this, el, event);
16179 onMonthChange: function () {
16183 onMoreEventClick: function(e, el, more)
16187 this.calpopover.placement = 'right';
16188 this.calpopover.setTitle('More');
16190 this.calpopover.setContent('');
16192 var ctr = this.calpopover.el.select('.popover-content', true).first();
16194 Roo.each(more, function(m){
16196 cls : 'fc-event-hori fc-event-draggable',
16199 var cg = ctr.createChild(cfg);
16201 cg.on('click', _this.onEventClick, _this, m);
16204 this.calpopover.show(el);
16209 onLoad: function ()
16211 this.calevents = [];
16214 if(this.store.getCount() > 0){
16215 this.store.data.each(function(d){
16218 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16219 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16220 time : d.data.start_time,
16221 title : d.data.title,
16222 description : d.data.description,
16223 venue : d.data.venue
16228 this.renderEvents();
16230 if(this.calevents.length && this.loadMask){
16231 this.maskEl.hide();
16235 onBeforeLoad: function()
16237 this.clearEvents();
16239 this.maskEl.show();
16253 * @class Roo.bootstrap.Popover
16254 * @extends Roo.bootstrap.Component
16255 * Bootstrap Popover class
16256 * @cfg {String} html contents of the popover (or false to use children..)
16257 * @cfg {String} title of popover (or false to hide)
16258 * @cfg {String} placement how it is placed
16259 * @cfg {String} trigger click || hover (or false to trigger manually)
16260 * @cfg {String} over what (parent or false to trigger manually.)
16261 * @cfg {Number} delay - delay before showing
16264 * Create a new Popover
16265 * @param {Object} config The config object
16268 Roo.bootstrap.Popover = function(config){
16269 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16275 * After the popover show
16277 * @param {Roo.bootstrap.Popover} this
16282 * After the popover hide
16284 * @param {Roo.bootstrap.Popover} this
16290 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16292 title: 'Fill in a title',
16295 placement : 'right',
16296 trigger : 'hover', // hover
16302 can_build_overlaid : false,
16304 getChildContainer : function()
16306 return this.el.select('.popover-content',true).first();
16309 getAutoCreate : function(){
16312 cls : 'popover roo-dynamic',
16313 style: 'display:block',
16319 cls : 'popover-inner',
16323 cls: 'popover-title',
16327 cls : 'popover-content',
16338 setTitle: function(str)
16341 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16343 setContent: function(str)
16346 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16348 // as it get's added to the bottom of the page.
16349 onRender : function(ct, position)
16351 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16353 var cfg = Roo.apply({}, this.getAutoCreate());
16357 cfg.cls += ' ' + this.cls;
16360 cfg.style = this.style;
16362 //Roo.log("adding to ");
16363 this.el = Roo.get(document.body).createChild(cfg, position);
16364 // Roo.log(this.el);
16369 initEvents : function()
16371 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16372 this.el.enableDisplayMode('block');
16374 if (this.over === false) {
16377 if (this.triggers === false) {
16380 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16381 var triggers = this.trigger ? this.trigger.split(' ') : [];
16382 Roo.each(triggers, function(trigger) {
16384 if (trigger == 'click') {
16385 on_el.on('click', this.toggle, this);
16386 } else if (trigger != 'manual') {
16387 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16388 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16390 on_el.on(eventIn ,this.enter, this);
16391 on_el.on(eventOut, this.leave, this);
16402 toggle : function () {
16403 this.hoverState == 'in' ? this.leave() : this.enter();
16406 enter : function () {
16408 clearTimeout(this.timeout);
16410 this.hoverState = 'in';
16412 if (!this.delay || !this.delay.show) {
16417 this.timeout = setTimeout(function () {
16418 if (_t.hoverState == 'in') {
16421 }, this.delay.show)
16424 leave : function() {
16425 clearTimeout(this.timeout);
16427 this.hoverState = 'out';
16429 if (!this.delay || !this.delay.hide) {
16434 this.timeout = setTimeout(function () {
16435 if (_t.hoverState == 'out') {
16438 }, this.delay.hide)
16441 show : function (on_el)
16444 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16448 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16449 if (this.html !== false) {
16450 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16452 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16453 if (!this.title.length) {
16454 this.el.select('.popover-title',true).hide();
16457 var placement = typeof this.placement == 'function' ?
16458 this.placement.call(this, this.el, on_el) :
16461 var autoToken = /\s?auto?\s?/i;
16462 var autoPlace = autoToken.test(placement);
16464 placement = placement.replace(autoToken, '') || 'top';
16468 //this.el.setXY([0,0]);
16470 this.el.dom.style.display='block';
16471 this.el.addClass(placement);
16473 //this.el.appendTo(on_el);
16475 var p = this.getPosition();
16476 var box = this.el.getBox();
16481 var align = Roo.bootstrap.Popover.alignment[placement];
16482 this.el.alignTo(on_el, align[0],align[1]);
16483 //var arrow = this.el.select('.arrow',true).first();
16484 //arrow.set(align[2],
16486 this.el.addClass('in');
16489 if (this.el.hasClass('fade')) {
16493 this.hoverState = 'in';
16495 this.fireEvent('show', this);
16500 this.el.setXY([0,0]);
16501 this.el.removeClass('in');
16503 this.hoverState = null;
16505 this.fireEvent('hide', this);
16510 Roo.bootstrap.Popover.alignment = {
16511 'left' : ['r-l', [-10,0], 'right'],
16512 'right' : ['l-r', [10,0], 'left'],
16513 'bottom' : ['t-b', [0,10], 'top'],
16514 'top' : [ 'b-t', [0,-10], 'bottom']
16525 * @class Roo.bootstrap.Progress
16526 * @extends Roo.bootstrap.Component
16527 * Bootstrap Progress class
16528 * @cfg {Boolean} striped striped of the progress bar
16529 * @cfg {Boolean} active animated of the progress bar
16533 * Create a new Progress
16534 * @param {Object} config The config object
16537 Roo.bootstrap.Progress = function(config){
16538 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16541 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16546 getAutoCreate : function(){
16554 cfg.cls += ' progress-striped';
16558 cfg.cls += ' active';
16577 * @class Roo.bootstrap.ProgressBar
16578 * @extends Roo.bootstrap.Component
16579 * Bootstrap ProgressBar class
16580 * @cfg {Number} aria_valuenow aria-value now
16581 * @cfg {Number} aria_valuemin aria-value min
16582 * @cfg {Number} aria_valuemax aria-value max
16583 * @cfg {String} label label for the progress bar
16584 * @cfg {String} panel (success | info | warning | danger )
16585 * @cfg {String} role role of the progress bar
16586 * @cfg {String} sr_only text
16590 * Create a new ProgressBar
16591 * @param {Object} config The config object
16594 Roo.bootstrap.ProgressBar = function(config){
16595 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16598 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16602 aria_valuemax : 100,
16608 getAutoCreate : function()
16613 cls: 'progress-bar',
16614 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16626 cfg.role = this.role;
16629 if(this.aria_valuenow){
16630 cfg['aria-valuenow'] = this.aria_valuenow;
16633 if(this.aria_valuemin){
16634 cfg['aria-valuemin'] = this.aria_valuemin;
16637 if(this.aria_valuemax){
16638 cfg['aria-valuemax'] = this.aria_valuemax;
16641 if(this.label && !this.sr_only){
16642 cfg.html = this.label;
16646 cfg.cls += ' progress-bar-' + this.panel;
16652 update : function(aria_valuenow)
16654 this.aria_valuenow = aria_valuenow;
16656 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16671 * @class Roo.bootstrap.TabGroup
16672 * @extends Roo.bootstrap.Column
16673 * Bootstrap Column class
16674 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16675 * @cfg {Boolean} carousel true to make the group behave like a carousel
16676 * @cfg {Boolean} bullets show bullets for the panels
16677 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16678 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16679 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16680 * @cfg {Boolean} showarrow (true|false) show arrow default true
16683 * Create a new TabGroup
16684 * @param {Object} config The config object
16687 Roo.bootstrap.TabGroup = function(config){
16688 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16690 this.navId = Roo.id();
16693 Roo.bootstrap.TabGroup.register(this);
16697 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16700 transition : false,
16705 slideOnTouch : false,
16708 getAutoCreate : function()
16710 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16712 cfg.cls += ' tab-content';
16714 if (this.carousel) {
16715 cfg.cls += ' carousel slide';
16718 cls : 'carousel-inner',
16722 if(this.bullets && !Roo.isTouch){
16725 cls : 'carousel-bullets',
16729 if(this.bullets_cls){
16730 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16737 cfg.cn[0].cn.push(bullets);
16740 if(this.showarrow){
16741 cfg.cn[0].cn.push({
16743 class : 'carousel-arrow',
16747 class : 'carousel-prev',
16751 class : 'fa fa-chevron-left'
16757 class : 'carousel-next',
16761 class : 'fa fa-chevron-right'
16774 initEvents: function()
16776 if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16777 this.el.on("touchstart", this.onTouchStart, this);
16780 if(this.autoslide){
16783 this.slideFn = window.setInterval(function() {
16784 _this.showPanelNext();
16788 if(this.showarrow){
16789 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16790 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16796 onTouchStart : function(e, el, o)
16798 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16802 this.showPanelNext();
16805 getChildContainer : function()
16807 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16811 * register a Navigation item
16812 * @param {Roo.bootstrap.NavItem} the navitem to add
16814 register : function(item)
16816 this.tabs.push( item);
16817 item.navId = this.navId; // not really needed..
16822 getActivePanel : function()
16825 Roo.each(this.tabs, function(t) {
16835 getPanelByName : function(n)
16838 Roo.each(this.tabs, function(t) {
16839 if (t.tabId == n) {
16847 indexOfPanel : function(p)
16850 Roo.each(this.tabs, function(t,i) {
16851 if (t.tabId == p.tabId) {
16860 * show a specific panel
16861 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16862 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16864 showPanel : function (pan)
16866 if(this.transition || typeof(pan) == 'undefined'){
16867 Roo.log("waiting for the transitionend");
16871 if (typeof(pan) == 'number') {
16872 pan = this.tabs[pan];
16875 if (typeof(pan) == 'string') {
16876 pan = this.getPanelByName(pan);
16879 var cur = this.getActivePanel();
16882 Roo.log('pan or acitve pan is undefined');
16886 if (pan.tabId == this.getActivePanel().tabId) {
16890 if (false === cur.fireEvent('beforedeactivate')) {
16894 if(this.bullets > 0 && !Roo.isTouch){
16895 this.setActiveBullet(this.indexOfPanel(pan));
16898 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16900 this.transition = true;
16901 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16902 var lr = dir == 'next' ? 'left' : 'right';
16903 pan.el.addClass(dir); // or prev
16904 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16905 cur.el.addClass(lr); // or right
16906 pan.el.addClass(lr);
16909 cur.el.on('transitionend', function() {
16910 Roo.log("trans end?");
16912 pan.el.removeClass([lr,dir]);
16913 pan.setActive(true);
16915 cur.el.removeClass([lr]);
16916 cur.setActive(false);
16918 _this.transition = false;
16920 }, this, { single: true } );
16925 cur.setActive(false);
16926 pan.setActive(true);
16931 showPanelNext : function()
16933 var i = this.indexOfPanel(this.getActivePanel());
16935 if (i >= this.tabs.length - 1 && !this.autoslide) {
16939 if (i >= this.tabs.length - 1 && this.autoslide) {
16943 this.showPanel(this.tabs[i+1]);
16946 showPanelPrev : function()
16948 var i = this.indexOfPanel(this.getActivePanel());
16950 if (i < 1 && !this.autoslide) {
16954 if (i < 1 && this.autoslide) {
16955 i = this.tabs.length;
16958 this.showPanel(this.tabs[i-1]);
16962 addBullet: function()
16964 if(!this.bullets || Roo.isTouch){
16967 var ctr = this.el.select('.carousel-bullets',true).first();
16968 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16969 var bullet = ctr.createChild({
16970 cls : 'bullet bullet-' + i
16971 },ctr.dom.lastChild);
16976 bullet.on('click', (function(e, el, o, ii, t){
16978 e.preventDefault();
16980 this.showPanel(ii);
16982 if(this.autoslide && this.slideFn){
16983 clearInterval(this.slideFn);
16984 this.slideFn = window.setInterval(function() {
16985 _this.showPanelNext();
16989 }).createDelegate(this, [i, bullet], true));
16994 setActiveBullet : function(i)
17000 Roo.each(this.el.select('.bullet', true).elements, function(el){
17001 el.removeClass('selected');
17004 var bullet = this.el.select('.bullet-' + i, true).first();
17010 bullet.addClass('selected');
17021 Roo.apply(Roo.bootstrap.TabGroup, {
17025 * register a Navigation Group
17026 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17028 register : function(navgrp)
17030 this.groups[navgrp.navId] = navgrp;
17034 * fetch a Navigation Group based on the navigation ID
17035 * if one does not exist , it will get created.
17036 * @param {string} the navgroup to add
17037 * @returns {Roo.bootstrap.NavGroup} the navgroup
17039 get: function(navId) {
17040 if (typeof(this.groups[navId]) == 'undefined') {
17041 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17043 return this.groups[navId] ;
17058 * @class Roo.bootstrap.TabPanel
17059 * @extends Roo.bootstrap.Component
17060 * Bootstrap TabPanel class
17061 * @cfg {Boolean} active panel active
17062 * @cfg {String} html panel content
17063 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17064 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17065 * @cfg {String} href click to link..
17069 * Create a new TabPanel
17070 * @param {Object} config The config object
17073 Roo.bootstrap.TabPanel = function(config){
17074 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17078 * Fires when the active status changes
17079 * @param {Roo.bootstrap.TabPanel} this
17080 * @param {Boolean} state the new state
17085 * @event beforedeactivate
17086 * Fires before a tab is de-activated - can be used to do validation on a form.
17087 * @param {Roo.bootstrap.TabPanel} this
17088 * @return {Boolean} false if there is an error
17091 'beforedeactivate': true
17094 this.tabId = this.tabId || Roo.id();
17098 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17106 getAutoCreate : function(){
17109 // item is needed for carousel - not sure if it has any effect otherwise
17110 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17111 html: this.html || ''
17115 cfg.cls += ' active';
17119 cfg.tabId = this.tabId;
17126 initEvents: function()
17128 var p = this.parent();
17129 this.navId = this.navId || p.navId;
17131 if (typeof(this.navId) != 'undefined') {
17132 // not really needed.. but just in case.. parent should be a NavGroup.
17133 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17137 var i = tg.tabs.length - 1;
17139 if(this.active && tg.bullets > 0 && i < tg.bullets){
17140 tg.setActiveBullet(i);
17144 if(this.href.length){
17145 this.el.on('click', this.onClick, this);
17150 onRender : function(ct, position)
17152 // Roo.log("Call onRender: " + this.xtype);
17154 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17162 setActive: function(state)
17164 Roo.log("panel - set active " + this.tabId + "=" + state);
17166 this.active = state;
17168 this.el.removeClass('active');
17170 } else if (!this.el.hasClass('active')) {
17171 this.el.addClass('active');
17174 this.fireEvent('changed', this, state);
17177 onClick: function(e)
17179 e.preventDefault();
17181 window.location.href = this.href;
17198 * @class Roo.bootstrap.DateField
17199 * @extends Roo.bootstrap.Input
17200 * Bootstrap DateField class
17201 * @cfg {Number} weekStart default 0
17202 * @cfg {String} viewMode default empty, (months|years)
17203 * @cfg {String} minViewMode default empty, (months|years)
17204 * @cfg {Number} startDate default -Infinity
17205 * @cfg {Number} endDate default Infinity
17206 * @cfg {Boolean} todayHighlight default false
17207 * @cfg {Boolean} todayBtn default false
17208 * @cfg {Boolean} calendarWeeks default false
17209 * @cfg {Object} daysOfWeekDisabled default empty
17210 * @cfg {Boolean} singleMode default false (true | false)
17212 * @cfg {Boolean} keyboardNavigation default true
17213 * @cfg {String} language default en
17216 * Create a new DateField
17217 * @param {Object} config The config object
17220 Roo.bootstrap.DateField = function(config){
17221 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17225 * Fires when this field show.
17226 * @param {Roo.bootstrap.DateField} this
17227 * @param {Mixed} date The date value
17232 * Fires when this field hide.
17233 * @param {Roo.bootstrap.DateField} this
17234 * @param {Mixed} date The date value
17239 * Fires when select a date.
17240 * @param {Roo.bootstrap.DateField} this
17241 * @param {Mixed} date The date value
17245 * @event beforeselect
17246 * Fires when before select a date.
17247 * @param {Roo.bootstrap.DateField} this
17248 * @param {Mixed} date The date value
17250 beforeselect : true
17254 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
17257 * @cfg {String} format
17258 * The default date format string which can be overriden for localization support. The format must be
17259 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17263 * @cfg {String} altFormats
17264 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17265 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17267 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17275 todayHighlight : false,
17281 keyboardNavigation: true,
17283 calendarWeeks: false,
17285 startDate: -Infinity,
17289 daysOfWeekDisabled: [],
17293 singleMode : false,
17295 UTCDate: function()
17297 return new Date(Date.UTC.apply(Date, arguments));
17300 UTCToday: function()
17302 var today = new Date();
17303 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17306 getDate: function() {
17307 var d = this.getUTCDate();
17308 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17311 getUTCDate: function() {
17315 setDate: function(d) {
17316 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17319 setUTCDate: function(d) {
17321 this.setValue(this.formatDate(this.date));
17324 onRender: function(ct, position)
17327 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17329 this.language = this.language || 'en';
17330 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17331 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17333 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17334 this.format = this.format || 'm/d/y';
17335 this.isInline = false;
17336 this.isInput = true;
17337 this.component = this.el.select('.add-on', true).first() || false;
17338 this.component = (this.component && this.component.length === 0) ? false : this.component;
17339 this.hasInput = this.component && this.inputEl().length;
17341 if (typeof(this.minViewMode === 'string')) {
17342 switch (this.minViewMode) {
17344 this.minViewMode = 1;
17347 this.minViewMode = 2;
17350 this.minViewMode = 0;
17355 if (typeof(this.viewMode === 'string')) {
17356 switch (this.viewMode) {
17369 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17371 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17373 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17375 this.picker().on('mousedown', this.onMousedown, this);
17376 this.picker().on('click', this.onClick, this);
17378 this.picker().addClass('datepicker-dropdown');
17380 this.startViewMode = this.viewMode;
17382 if(this.singleMode){
17383 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17384 v.setVisibilityMode(Roo.Element.DISPLAY);
17388 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17389 v.setStyle('width', '189px');
17393 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17394 if(!this.calendarWeeks){
17399 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17400 v.attr('colspan', function(i, val){
17401 return parseInt(val) + 1;
17406 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17408 this.setStartDate(this.startDate);
17409 this.setEndDate(this.endDate);
17411 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17418 if(this.isInline) {
17423 picker : function()
17425 return this.pickerEl;
17426 // return this.el.select('.datepicker', true).first();
17429 fillDow: function()
17431 var dowCnt = this.weekStart;
17440 if(this.calendarWeeks){
17448 while (dowCnt < this.weekStart + 7) {
17452 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17456 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17459 fillMonths: function()
17462 var months = this.picker().select('>.datepicker-months td', true).first();
17464 months.dom.innerHTML = '';
17470 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17473 months.createChild(month);
17480 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;
17482 if (this.date < this.startDate) {
17483 this.viewDate = new Date(this.startDate);
17484 } else if (this.date > this.endDate) {
17485 this.viewDate = new Date(this.endDate);
17487 this.viewDate = new Date(this.date);
17495 var d = new Date(this.viewDate),
17496 year = d.getUTCFullYear(),
17497 month = d.getUTCMonth(),
17498 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17499 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17500 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17501 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17502 currentDate = this.date && this.date.valueOf(),
17503 today = this.UTCToday();
17505 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17507 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17509 // this.picker.select('>tfoot th.today').
17510 // .text(dates[this.language].today)
17511 // .toggle(this.todayBtn !== false);
17513 this.updateNavArrows();
17516 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17518 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17520 prevMonth.setUTCDate(day);
17522 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17524 var nextMonth = new Date(prevMonth);
17526 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17528 nextMonth = nextMonth.valueOf();
17530 var fillMonths = false;
17532 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17534 while(prevMonth.valueOf() < nextMonth) {
17537 if (prevMonth.getUTCDay() === this.weekStart) {
17539 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17547 if(this.calendarWeeks){
17548 // ISO 8601: First week contains first thursday.
17549 // ISO also states week starts on Monday, but we can be more abstract here.
17551 // Start of current week: based on weekstart/current date
17552 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17553 // Thursday of this week
17554 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17555 // First Thursday of year, year from thursday
17556 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17557 // Calendar week: ms between thursdays, div ms per day, div 7 days
17558 calWeek = (th - yth) / 864e5 / 7 + 1;
17560 fillMonths.cn.push({
17568 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17570 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17573 if (this.todayHighlight &&
17574 prevMonth.getUTCFullYear() == today.getFullYear() &&
17575 prevMonth.getUTCMonth() == today.getMonth() &&
17576 prevMonth.getUTCDate() == today.getDate()) {
17577 clsName += ' today';
17580 if (currentDate && prevMonth.valueOf() === currentDate) {
17581 clsName += ' active';
17584 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17585 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17586 clsName += ' disabled';
17589 fillMonths.cn.push({
17591 cls: 'day ' + clsName,
17592 html: prevMonth.getDate()
17595 prevMonth.setDate(prevMonth.getDate()+1);
17598 var currentYear = this.date && this.date.getUTCFullYear();
17599 var currentMonth = this.date && this.date.getUTCMonth();
17601 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17603 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17604 v.removeClass('active');
17606 if(currentYear === year && k === currentMonth){
17607 v.addClass('active');
17610 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17611 v.addClass('disabled');
17617 year = parseInt(year/10, 10) * 10;
17619 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17621 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17624 for (var i = -1; i < 11; i++) {
17625 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17627 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17635 showMode: function(dir)
17638 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17641 Roo.each(this.picker().select('>div',true).elements, function(v){
17642 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17645 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17650 if(this.isInline) {
17654 this.picker().removeClass(['bottom', 'top']);
17656 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17658 * place to the top of element!
17662 this.picker().addClass('top');
17663 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17668 this.picker().addClass('bottom');
17670 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17673 parseDate : function(value)
17675 if(!value || value instanceof Date){
17678 var v = Date.parseDate(value, this.format);
17679 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17680 v = Date.parseDate(value, 'Y-m-d');
17682 if(!v && this.altFormats){
17683 if(!this.altFormatsArray){
17684 this.altFormatsArray = this.altFormats.split("|");
17686 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17687 v = Date.parseDate(value, this.altFormatsArray[i]);
17693 formatDate : function(date, fmt)
17695 return (!date || !(date instanceof Date)) ?
17696 date : date.dateFormat(fmt || this.format);
17699 onFocus : function()
17701 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17705 onBlur : function()
17707 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17709 var d = this.inputEl().getValue();
17718 this.picker().show();
17722 this.fireEvent('show', this, this.date);
17727 if(this.isInline) {
17730 this.picker().hide();
17731 this.viewMode = this.startViewMode;
17734 this.fireEvent('hide', this, this.date);
17738 onMousedown: function(e)
17740 e.stopPropagation();
17741 e.preventDefault();
17746 Roo.bootstrap.DateField.superclass.keyup.call(this);
17750 setValue: function(v)
17752 if(this.fireEvent('beforeselect', this, v) !== false){
17753 var d = new Date(this.parseDate(v) ).clearTime();
17755 if(isNaN(d.getTime())){
17756 this.date = this.viewDate = '';
17757 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17761 v = this.formatDate(d);
17763 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17765 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17769 this.fireEvent('select', this, this.date);
17773 getValue: function()
17775 return this.formatDate(this.date);
17778 fireKey: function(e)
17780 if (!this.picker().isVisible()){
17781 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17787 var dateChanged = false,
17789 newDate, newViewDate;
17794 e.preventDefault();
17798 if (!this.keyboardNavigation) {
17801 dir = e.keyCode == 37 ? -1 : 1;
17804 newDate = this.moveYear(this.date, dir);
17805 newViewDate = this.moveYear(this.viewDate, dir);
17806 } else if (e.shiftKey){
17807 newDate = this.moveMonth(this.date, dir);
17808 newViewDate = this.moveMonth(this.viewDate, dir);
17810 newDate = new Date(this.date);
17811 newDate.setUTCDate(this.date.getUTCDate() + dir);
17812 newViewDate = new Date(this.viewDate);
17813 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17815 if (this.dateWithinRange(newDate)){
17816 this.date = newDate;
17817 this.viewDate = newViewDate;
17818 this.setValue(this.formatDate(this.date));
17820 e.preventDefault();
17821 dateChanged = true;
17826 if (!this.keyboardNavigation) {
17829 dir = e.keyCode == 38 ? -1 : 1;
17831 newDate = this.moveYear(this.date, dir);
17832 newViewDate = this.moveYear(this.viewDate, dir);
17833 } else if (e.shiftKey){
17834 newDate = this.moveMonth(this.date, dir);
17835 newViewDate = this.moveMonth(this.viewDate, dir);
17837 newDate = new Date(this.date);
17838 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17839 newViewDate = new Date(this.viewDate);
17840 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17842 if (this.dateWithinRange(newDate)){
17843 this.date = newDate;
17844 this.viewDate = newViewDate;
17845 this.setValue(this.formatDate(this.date));
17847 e.preventDefault();
17848 dateChanged = true;
17852 this.setValue(this.formatDate(this.date));
17854 e.preventDefault();
17857 this.setValue(this.formatDate(this.date));
17871 onClick: function(e)
17873 e.stopPropagation();
17874 e.preventDefault();
17876 var target = e.getTarget();
17878 if(target.nodeName.toLowerCase() === 'i'){
17879 target = Roo.get(target).dom.parentNode;
17882 var nodeName = target.nodeName;
17883 var className = target.className;
17884 var html = target.innerHTML;
17885 //Roo.log(nodeName);
17887 switch(nodeName.toLowerCase()) {
17889 switch(className) {
17895 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17896 switch(this.viewMode){
17898 this.viewDate = this.moveMonth(this.viewDate, dir);
17902 this.viewDate = this.moveYear(this.viewDate, dir);
17908 var date = new Date();
17909 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17911 this.setValue(this.formatDate(this.date));
17918 if (className.indexOf('disabled') < 0) {
17919 this.viewDate.setUTCDate(1);
17920 if (className.indexOf('month') > -1) {
17921 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17923 var year = parseInt(html, 10) || 0;
17924 this.viewDate.setUTCFullYear(year);
17928 if(this.singleMode){
17929 this.setValue(this.formatDate(this.viewDate));
17940 //Roo.log(className);
17941 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17942 var day = parseInt(html, 10) || 1;
17943 var year = this.viewDate.getUTCFullYear(),
17944 month = this.viewDate.getUTCMonth();
17946 if (className.indexOf('old') > -1) {
17953 } else if (className.indexOf('new') > -1) {
17961 //Roo.log([year,month,day]);
17962 this.date = this.UTCDate(year, month, day,0,0,0,0);
17963 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17965 //Roo.log(this.formatDate(this.date));
17966 this.setValue(this.formatDate(this.date));
17973 setStartDate: function(startDate)
17975 this.startDate = startDate || -Infinity;
17976 if (this.startDate !== -Infinity) {
17977 this.startDate = this.parseDate(this.startDate);
17980 this.updateNavArrows();
17983 setEndDate: function(endDate)
17985 this.endDate = endDate || Infinity;
17986 if (this.endDate !== Infinity) {
17987 this.endDate = this.parseDate(this.endDate);
17990 this.updateNavArrows();
17993 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17995 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17996 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17997 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17999 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18000 return parseInt(d, 10);
18003 this.updateNavArrows();
18006 updateNavArrows: function()
18008 if(this.singleMode){
18012 var d = new Date(this.viewDate),
18013 year = d.getUTCFullYear(),
18014 month = d.getUTCMonth();
18016 Roo.each(this.picker().select('.prev', true).elements, function(v){
18018 switch (this.viewMode) {
18021 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18027 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18034 Roo.each(this.picker().select('.next', true).elements, function(v){
18036 switch (this.viewMode) {
18039 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18045 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18053 moveMonth: function(date, dir)
18058 var new_date = new Date(date.valueOf()),
18059 day = new_date.getUTCDate(),
18060 month = new_date.getUTCMonth(),
18061 mag = Math.abs(dir),
18063 dir = dir > 0 ? 1 : -1;
18066 // If going back one month, make sure month is not current month
18067 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18069 return new_date.getUTCMonth() == month;
18071 // If going forward one month, make sure month is as expected
18072 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18074 return new_date.getUTCMonth() != new_month;
18076 new_month = month + dir;
18077 new_date.setUTCMonth(new_month);
18078 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18079 if (new_month < 0 || new_month > 11) {
18080 new_month = (new_month + 12) % 12;
18083 // For magnitudes >1, move one month at a time...
18084 for (var i=0; i<mag; i++) {
18085 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18086 new_date = this.moveMonth(new_date, dir);
18088 // ...then reset the day, keeping it in the new month
18089 new_month = new_date.getUTCMonth();
18090 new_date.setUTCDate(day);
18092 return new_month != new_date.getUTCMonth();
18095 // Common date-resetting loop -- if date is beyond end of month, make it
18098 new_date.setUTCDate(--day);
18099 new_date.setUTCMonth(new_month);
18104 moveYear: function(date, dir)
18106 return this.moveMonth(date, dir*12);
18109 dateWithinRange: function(date)
18111 return date >= this.startDate && date <= this.endDate;
18117 this.picker().remove();
18120 validateValue : function(value)
18122 if(value.length < 1) {
18123 if(this.allowBlank){
18129 if(value.length < this.minLength){
18132 if(value.length > this.maxLength){
18136 var vt = Roo.form.VTypes;
18137 if(!vt[this.vtype](value, this)){
18141 if(typeof this.validator == "function"){
18142 var msg = this.validator(value);
18148 if(this.regex && !this.regex.test(value)){
18152 if(typeof(this.parseDate(value)) == 'undefined'){
18156 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18160 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18170 Roo.apply(Roo.bootstrap.DateField, {
18181 html: '<i class="fa fa-arrow-left"/>'
18191 html: '<i class="fa fa-arrow-right"/>'
18233 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18234 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18235 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18236 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18237 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18250 navFnc: 'FullYear',
18255 navFnc: 'FullYear',
18260 Roo.apply(Roo.bootstrap.DateField, {
18264 cls: 'datepicker dropdown-menu roo-dynamic',
18268 cls: 'datepicker-days',
18272 cls: 'table-condensed',
18274 Roo.bootstrap.DateField.head,
18278 Roo.bootstrap.DateField.footer
18285 cls: 'datepicker-months',
18289 cls: 'table-condensed',
18291 Roo.bootstrap.DateField.head,
18292 Roo.bootstrap.DateField.content,
18293 Roo.bootstrap.DateField.footer
18300 cls: 'datepicker-years',
18304 cls: 'table-condensed',
18306 Roo.bootstrap.DateField.head,
18307 Roo.bootstrap.DateField.content,
18308 Roo.bootstrap.DateField.footer
18327 * @class Roo.bootstrap.TimeField
18328 * @extends Roo.bootstrap.Input
18329 * Bootstrap DateField class
18333 * Create a new TimeField
18334 * @param {Object} config The config object
18337 Roo.bootstrap.TimeField = function(config){
18338 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18342 * Fires when this field show.
18343 * @param {Roo.bootstrap.DateField} thisthis
18344 * @param {Mixed} date The date value
18349 * Fires when this field hide.
18350 * @param {Roo.bootstrap.DateField} this
18351 * @param {Mixed} date The date value
18356 * Fires when select a date.
18357 * @param {Roo.bootstrap.DateField} this
18358 * @param {Mixed} date The date value
18364 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18367 * @cfg {String} format
18368 * The default time format string which can be overriden for localization support. The format must be
18369 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18373 onRender: function(ct, position)
18376 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18378 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18380 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18382 this.pop = this.picker().select('>.datepicker-time',true).first();
18383 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18385 this.picker().on('mousedown', this.onMousedown, this);
18386 this.picker().on('click', this.onClick, this);
18388 this.picker().addClass('datepicker-dropdown');
18393 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18394 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18395 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18396 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18397 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18398 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18402 fireKey: function(e){
18403 if (!this.picker().isVisible()){
18404 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18410 e.preventDefault();
18418 this.onTogglePeriod();
18421 this.onIncrementMinutes();
18424 this.onDecrementMinutes();
18433 onClick: function(e) {
18434 e.stopPropagation();
18435 e.preventDefault();
18438 picker : function()
18440 return this.el.select('.datepicker', true).first();
18443 fillTime: function()
18445 var time = this.pop.select('tbody', true).first();
18447 time.dom.innerHTML = '';
18462 cls: 'hours-up glyphicon glyphicon-chevron-up'
18482 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18503 cls: 'timepicker-hour',
18518 cls: 'timepicker-minute',
18533 cls: 'btn btn-primary period',
18555 cls: 'hours-down glyphicon glyphicon-chevron-down'
18575 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18593 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18600 var hours = this.time.getHours();
18601 var minutes = this.time.getMinutes();
18614 hours = hours - 12;
18618 hours = '0' + hours;
18622 minutes = '0' + minutes;
18625 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18626 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18627 this.pop.select('button', true).first().dom.innerHTML = period;
18633 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18635 var cls = ['bottom'];
18637 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18644 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18649 this.picker().addClass(cls.join('-'));
18653 Roo.each(cls, function(c){
18655 _this.picker().setTop(_this.inputEl().getHeight());
18659 _this.picker().setTop(0 - _this.picker().getHeight());
18664 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18668 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18675 onFocus : function()
18677 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18681 onBlur : function()
18683 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18689 this.picker().show();
18694 this.fireEvent('show', this, this.date);
18699 this.picker().hide();
18702 this.fireEvent('hide', this, this.date);
18705 setTime : function()
18708 this.setValue(this.time.format(this.format));
18710 this.fireEvent('select', this, this.date);
18715 onMousedown: function(e){
18716 e.stopPropagation();
18717 e.preventDefault();
18720 onIncrementHours: function()
18722 Roo.log('onIncrementHours');
18723 this.time = this.time.add(Date.HOUR, 1);
18728 onDecrementHours: function()
18730 Roo.log('onDecrementHours');
18731 this.time = this.time.add(Date.HOUR, -1);
18735 onIncrementMinutes: function()
18737 Roo.log('onIncrementMinutes');
18738 this.time = this.time.add(Date.MINUTE, 1);
18742 onDecrementMinutes: function()
18744 Roo.log('onDecrementMinutes');
18745 this.time = this.time.add(Date.MINUTE, -1);
18749 onTogglePeriod: function()
18751 Roo.log('onTogglePeriod');
18752 this.time = this.time.add(Date.HOUR, 12);
18759 Roo.apply(Roo.bootstrap.TimeField, {
18789 cls: 'btn btn-info ok',
18801 Roo.apply(Roo.bootstrap.TimeField, {
18805 cls: 'datepicker dropdown-menu',
18809 cls: 'datepicker-time',
18813 cls: 'table-condensed',
18815 Roo.bootstrap.TimeField.content,
18816 Roo.bootstrap.TimeField.footer
18835 * @class Roo.bootstrap.MonthField
18836 * @extends Roo.bootstrap.Input
18837 * Bootstrap MonthField class
18839 * @cfg {String} language default en
18842 * Create a new MonthField
18843 * @param {Object} config The config object
18846 Roo.bootstrap.MonthField = function(config){
18847 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18852 * Fires when this field show.
18853 * @param {Roo.bootstrap.MonthField} this
18854 * @param {Mixed} date The date value
18859 * Fires when this field hide.
18860 * @param {Roo.bootstrap.MonthField} this
18861 * @param {Mixed} date The date value
18866 * Fires when select a date.
18867 * @param {Roo.bootstrap.MonthField} this
18868 * @param {String} oldvalue The old value
18869 * @param {String} newvalue The new value
18875 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18877 onRender: function(ct, position)
18880 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18882 this.language = this.language || 'en';
18883 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18884 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18886 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18887 this.isInline = false;
18888 this.isInput = true;
18889 this.component = this.el.select('.add-on', true).first() || false;
18890 this.component = (this.component && this.component.length === 0) ? false : this.component;
18891 this.hasInput = this.component && this.inputEL().length;
18893 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18895 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18897 this.picker().on('mousedown', this.onMousedown, this);
18898 this.picker().on('click', this.onClick, this);
18900 this.picker().addClass('datepicker-dropdown');
18902 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18903 v.setStyle('width', '189px');
18910 if(this.isInline) {
18916 setValue: function(v, suppressEvent)
18918 var o = this.getValue();
18920 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18924 if(suppressEvent !== true){
18925 this.fireEvent('select', this, o, v);
18930 getValue: function()
18935 onClick: function(e)
18937 e.stopPropagation();
18938 e.preventDefault();
18940 var target = e.getTarget();
18942 if(target.nodeName.toLowerCase() === 'i'){
18943 target = Roo.get(target).dom.parentNode;
18946 var nodeName = target.nodeName;
18947 var className = target.className;
18948 var html = target.innerHTML;
18950 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18954 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18956 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18962 picker : function()
18964 return this.pickerEl;
18967 fillMonths: function()
18970 var months = this.picker().select('>.datepicker-months td', true).first();
18972 months.dom.innerHTML = '';
18978 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18981 months.createChild(month);
18990 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18991 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18994 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18995 e.removeClass('active');
18997 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18998 e.addClass('active');
19005 if(this.isInline) {
19009 this.picker().removeClass(['bottom', 'top']);
19011 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19013 * place to the top of element!
19017 this.picker().addClass('top');
19018 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19023 this.picker().addClass('bottom');
19025 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19028 onFocus : function()
19030 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19034 onBlur : function()
19036 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19038 var d = this.inputEl().getValue();
19047 this.picker().show();
19048 this.picker().select('>.datepicker-months', true).first().show();
19052 this.fireEvent('show', this, this.date);
19057 if(this.isInline) {
19060 this.picker().hide();
19061 this.fireEvent('hide', this, this.date);
19065 onMousedown: function(e)
19067 e.stopPropagation();
19068 e.preventDefault();
19073 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19077 fireKey: function(e)
19079 if (!this.picker().isVisible()){
19080 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19091 e.preventDefault();
19095 dir = e.keyCode == 37 ? -1 : 1;
19097 this.vIndex = this.vIndex + dir;
19099 if(this.vIndex < 0){
19103 if(this.vIndex > 11){
19107 if(isNaN(this.vIndex)){
19111 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19117 dir = e.keyCode == 38 ? -1 : 1;
19119 this.vIndex = this.vIndex + dir * 4;
19121 if(this.vIndex < 0){
19125 if(this.vIndex > 11){
19129 if(isNaN(this.vIndex)){
19133 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19138 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19139 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19143 e.preventDefault();
19146 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19147 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19163 this.picker().remove();
19168 Roo.apply(Roo.bootstrap.MonthField, {
19187 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19188 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19193 Roo.apply(Roo.bootstrap.MonthField, {
19197 cls: 'datepicker dropdown-menu roo-dynamic',
19201 cls: 'datepicker-months',
19205 cls: 'table-condensed',
19207 Roo.bootstrap.DateField.content
19227 * @class Roo.bootstrap.CheckBox
19228 * @extends Roo.bootstrap.Input
19229 * Bootstrap CheckBox class
19231 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19232 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19233 * @cfg {String} boxLabel The text that appears beside the checkbox
19234 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19235 * @cfg {Boolean} checked initnal the element
19236 * @cfg {Boolean} inline inline the element (default false)
19237 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19240 * Create a new CheckBox
19241 * @param {Object} config The config object
19244 Roo.bootstrap.CheckBox = function(config){
19245 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19250 * Fires when the element is checked or unchecked.
19251 * @param {Roo.bootstrap.CheckBox} this This input
19252 * @param {Boolean} checked The new checked value
19259 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
19261 inputType: 'checkbox',
19269 getAutoCreate : function()
19271 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19277 cfg.cls = 'form-group ' + this.inputType; //input-group
19280 cfg.cls += ' ' + this.inputType + '-inline';
19286 type : this.inputType,
19287 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
19288 cls : 'roo-' + this.inputType, //'form-box',
19289 placeholder : this.placeholder || ''
19293 if (this.weight) { // Validity check?
19294 cfg.cls += " " + this.inputType + "-" + this.weight;
19297 if (this.disabled) {
19298 input.disabled=true;
19302 input.checked = this.checked;
19306 input.name = this.name;
19310 input.cls += ' input-' + this.size;
19315 ['xs','sm','md','lg'].map(function(size){
19316 if (settings[size]) {
19317 cfg.cls += ' col-' + size + '-' + settings[size];
19321 var inputblock = input;
19323 if (this.before || this.after) {
19326 cls : 'input-group',
19331 inputblock.cn.push({
19333 cls : 'input-group-addon',
19338 inputblock.cn.push(input);
19341 inputblock.cn.push({
19343 cls : 'input-group-addon',
19350 if (align ==='left' && this.fieldLabel.length) {
19351 // Roo.log("left and has label");
19357 cls : 'control-label col-md-' + this.labelWidth,
19358 html : this.fieldLabel
19362 cls : "col-md-" + (12 - this.labelWidth),
19369 } else if ( this.fieldLabel.length) {
19370 // Roo.log(" label");
19374 tag: this.boxLabel ? 'span' : 'label',
19376 cls: 'control-label box-input-label',
19377 //cls : 'input-group-addon',
19378 html : this.fieldLabel
19388 // Roo.log(" no label && no align");
19389 cfg.cn = [ inputblock ] ;
19395 var boxLabelCfg = {
19397 //'for': id, // box label is handled by onclick - so no for...
19399 html: this.boxLabel
19403 boxLabelCfg.tooltip = this.tooltip;
19406 cfg.cn.push(boxLabelCfg);
19416 * return the real input element.
19418 inputEl: function ()
19420 return this.el.select('input.roo-' + this.inputType,true).first();
19423 labelEl: function()
19425 return this.el.select('label.control-label',true).first();
19427 /* depricated... */
19431 return this.labelEl();
19434 boxLabelEl: function()
19436 return this.el.select('label.box-label',true).first();
19439 initEvents : function()
19441 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19443 this.inputEl().on('click', this.onClick, this);
19445 if (this.boxLabel) {
19446 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19449 this.startValue = this.getValue();
19452 Roo.bootstrap.CheckBox.register(this);
19456 onClick : function()
19458 this.setChecked(!this.checked);
19461 setChecked : function(state,suppressEvent)
19463 this.startValue = this.getValue();
19465 if(this.inputType == 'radio'){
19467 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19468 e.dom.checked = false;
19471 this.inputEl().dom.checked = true;
19473 this.inputEl().dom.value = this.inputValue;
19475 if(suppressEvent !== true){
19476 this.fireEvent('check', this, true);
19484 this.checked = state;
19486 this.inputEl().dom.checked = state;
19488 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19490 if(suppressEvent !== true){
19491 this.fireEvent('check', this, state);
19497 getValue : function()
19499 if(this.inputType == 'radio'){
19500 return this.getGroupValue();
19503 return this.inputEl().getValue();
19507 getGroupValue : function()
19509 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19513 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19516 setValue : function(v,suppressEvent)
19518 if(this.inputType == 'radio'){
19519 this.setGroupValue(v, suppressEvent);
19523 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19528 setGroupValue : function(v, suppressEvent)
19530 this.startValue = this.getValue();
19532 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19533 e.dom.checked = false;
19535 if(e.dom.value == v){
19536 e.dom.checked = true;
19540 if(suppressEvent !== true){
19541 this.fireEvent('check', this, true);
19549 validate : function()
19553 (this.inputType == 'radio' && this.validateRadio()) ||
19554 (this.inputType == 'checkbox' && this.validateCheckbox())
19560 this.markInvalid();
19564 validateRadio : function()
19566 if(this.allowBlank){
19572 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19573 if(!e.dom.checked){
19585 validateCheckbox : function()
19588 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19591 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19599 for(var i in group){
19604 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19611 * Mark this field as valid
19613 markValid : function()
19615 if(this.allowBlank){
19621 this.fireEvent('valid', this);
19623 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19626 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19633 if(this.inputType == 'radio'){
19634 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19635 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19636 e.findParent('.form-group', false, true).addClass(_this.validClass);
19643 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19644 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19648 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19654 for(var i in group){
19655 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19656 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19661 * Mark this field as invalid
19662 * @param {String} msg The validation message
19664 markInvalid : function(msg)
19666 if(this.allowBlank){
19672 this.fireEvent('invalid', this, msg);
19674 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19677 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19681 label.markInvalid();
19684 if(this.inputType == 'radio'){
19685 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19686 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19687 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19694 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19695 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19699 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19705 for(var i in group){
19706 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19707 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19712 disable : function()
19714 if(this.inputType != 'radio'){
19715 Roo.bootstrap.CheckBox.superclass.disable.call(this);
19722 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19723 _this.getActionEl().addClass(this.disabledClass);
19724 e.dom.disabled = true;
19728 this.disabled = true;
19729 this.fireEvent("disable", this);
19733 enable : function()
19735 if(this.inputType != 'radio'){
19736 Roo.bootstrap.CheckBox.superclass.enable.call(this);
19743 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19744 _this.getActionEl().removeClass(this.disabledClass);
19745 e.dom.disabled = false;
19749 this.disabled = false;
19750 this.fireEvent("enable", this);
19757 Roo.apply(Roo.bootstrap.CheckBox, {
19762 * register a CheckBox Group
19763 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19765 register : function(checkbox)
19767 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19768 this.groups[checkbox.groupId] = {};
19771 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19775 this.groups[checkbox.groupId][checkbox.name] = checkbox;
19779 * fetch a CheckBox Group based on the group ID
19780 * @param {string} the group ID
19781 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19783 get: function(groupId) {
19784 if (typeof(this.groups[groupId]) == 'undefined') {
19788 return this.groups[groupId] ;
19800 *<div class="radio">
19802 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19803 Option one is this and that—be sure to include why it's great
19810 *<label class="radio-inline">fieldLabel</label>
19811 *<label class="radio-inline">
19812 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19820 * @class Roo.bootstrap.Radio
19821 * @extends Roo.bootstrap.CheckBox
19822 * Bootstrap Radio class
19825 * Create a new Radio
19826 * @param {Object} config The config object
19829 Roo.bootstrap.Radio = function(config){
19830 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19834 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19836 inputType: 'radio',
19840 getAutoCreate : function()
19842 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19843 align = align || 'left'; // default...
19850 tag : this.inline ? 'span' : 'div',
19855 var inline = this.inline ? ' radio-inline' : '';
19859 // does not need for, as we wrap the input with it..
19861 cls : 'control-label box-label' + inline,
19864 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19868 //cls : 'control-label' + inline,
19869 html : this.fieldLabel,
19870 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19879 type : this.inputType,
19880 //value : (!this.checked) ? this.valueOff : this.inputValue,
19881 value : this.inputValue,
19883 placeholder : this.placeholder || '' // ?? needed????
19886 if (this.weight) { // Validity check?
19887 input.cls += " radio-" + this.weight;
19889 if (this.disabled) {
19890 input.disabled=true;
19894 input.checked = this.checked;
19898 input.name = this.name;
19902 input.cls += ' input-' + this.size;
19905 //?? can span's inline have a width??
19908 ['xs','sm','md','lg'].map(function(size){
19909 if (settings[size]) {
19910 cfg.cls += ' col-' + size + '-' + settings[size];
19914 var inputblock = input;
19916 if (this.before || this.after) {
19919 cls : 'input-group',
19924 inputblock.cn.push({
19926 cls : 'input-group-addon',
19930 inputblock.cn.push(input);
19932 inputblock.cn.push({
19934 cls : 'input-group-addon',
19942 if (this.fieldLabel && this.fieldLabel.length) {
19943 cfg.cn.push(fieldLabel);
19946 // normal bootstrap puts the input inside the label.
19947 // however with our styled version - it has to go after the input.
19949 //lbl.cn.push(inputblock);
19953 cls: 'radio' + inline,
19960 cfg.cn.push( lblwrap);
19965 html: this.boxLabel
19974 initEvents : function()
19976 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19978 this.inputEl().on('click', this.onClick, this);
19979 if (this.boxLabel) {
19980 //Roo.log('find label');
19981 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19986 inputEl: function ()
19988 return this.el.select('input.roo-radio',true).first();
19990 onClick : function()
19993 this.setChecked(true);
19996 setChecked : function(state,suppressEvent)
19999 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20000 v.dom.checked = false;
20003 Roo.log(this.inputEl().dom);
20004 this.checked = state;
20005 this.inputEl().dom.checked = state;
20007 if(suppressEvent !== true){
20008 this.fireEvent('check', this, state);
20011 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20015 getGroupValue : function()
20018 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20019 if(v.dom.checked == true){
20020 value = v.dom.value;
20028 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
20029 * @return {Mixed} value The field value
20031 getValue : function(){
20032 return this.getGroupValue();
20038 //<script type="text/javascript">
20041 * Based Ext JS Library 1.1.1
20042 * Copyright(c) 2006-2007, Ext JS, LLC.
20048 * @class Roo.HtmlEditorCore
20049 * @extends Roo.Component
20050 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20052 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20055 Roo.HtmlEditorCore = function(config){
20058 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20063 * @event initialize
20064 * Fires when the editor is fully initialized (including the iframe)
20065 * @param {Roo.HtmlEditorCore} this
20070 * Fires when the editor is first receives the focus. Any insertion must wait
20071 * until after this event.
20072 * @param {Roo.HtmlEditorCore} this
20076 * @event beforesync
20077 * Fires before the textarea is updated with content from the editor iframe. Return false
20078 * to cancel the sync.
20079 * @param {Roo.HtmlEditorCore} this
20080 * @param {String} html
20084 * @event beforepush
20085 * Fires before the iframe editor is updated with content from the textarea. Return false
20086 * to cancel the push.
20087 * @param {Roo.HtmlEditorCore} this
20088 * @param {String} html
20093 * Fires when the textarea is updated with content from the editor iframe.
20094 * @param {Roo.HtmlEditorCore} this
20095 * @param {String} html
20100 * Fires when the iframe editor is updated with content from the textarea.
20101 * @param {Roo.HtmlEditorCore} this
20102 * @param {String} html
20107 * @event editorevent
20108 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20109 * @param {Roo.HtmlEditorCore} this
20115 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20117 // defaults : white / black...
20118 this.applyBlacklists();
20125 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20129 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20135 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20140 * @cfg {Number} height (in pixels)
20144 * @cfg {Number} width (in pixels)
20149 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20152 stylesheets: false,
20157 // private properties
20158 validationEvent : false,
20160 initialized : false,
20162 sourceEditMode : false,
20163 onFocus : Roo.emptyFn,
20165 hideMode:'offsets',
20169 // blacklist + whitelisted elements..
20176 * Protected method that will not generally be called directly. It
20177 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20178 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20180 getDocMarkup : function(){
20184 // inherit styels from page...??
20185 if (this.stylesheets === false) {
20187 Roo.get(document.head).select('style').each(function(node) {
20188 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20191 Roo.get(document.head).select('link').each(function(node) {
20192 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20195 } else if (!this.stylesheets.length) {
20197 st = '<style type="text/css">' +
20198 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20204 st += '<style type="text/css">' +
20205 'IMG { cursor: pointer } ' +
20209 return '<html><head>' + st +
20210 //<style type="text/css">' +
20211 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20213 ' </head><body class="roo-htmleditor-body"></body></html>';
20217 onRender : function(ct, position)
20220 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20221 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20224 this.el.dom.style.border = '0 none';
20225 this.el.dom.setAttribute('tabIndex', -1);
20226 this.el.addClass('x-hidden hide');
20230 if(Roo.isIE){ // fix IE 1px bogus margin
20231 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20235 this.frameId = Roo.id();
20239 var iframe = this.owner.wrap.createChild({
20241 cls: 'form-control', // bootstrap..
20243 name: this.frameId,
20244 frameBorder : 'no',
20245 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20250 this.iframe = iframe.dom;
20252 this.assignDocWin();
20254 this.doc.designMode = 'on';
20257 this.doc.write(this.getDocMarkup());
20261 var task = { // must defer to wait for browser to be ready
20263 //console.log("run task?" + this.doc.readyState);
20264 this.assignDocWin();
20265 if(this.doc.body || this.doc.readyState == 'complete'){
20267 this.doc.designMode="on";
20271 Roo.TaskMgr.stop(task);
20272 this.initEditor.defer(10, this);
20279 Roo.TaskMgr.start(task);
20284 onResize : function(w, h)
20286 Roo.log('resize: ' +w + ',' + h );
20287 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20291 if(typeof w == 'number'){
20293 this.iframe.style.width = w + 'px';
20295 if(typeof h == 'number'){
20297 this.iframe.style.height = h + 'px';
20299 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20306 * Toggles the editor between standard and source edit mode.
20307 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20309 toggleSourceEdit : function(sourceEditMode){
20311 this.sourceEditMode = sourceEditMode === true;
20313 if(this.sourceEditMode){
20315 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20318 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20319 //this.iframe.className = '';
20322 //this.setSize(this.owner.wrap.getSize());
20323 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20330 * Protected method that will not generally be called directly. If you need/want
20331 * custom HTML cleanup, this is the method you should override.
20332 * @param {String} html The HTML to be cleaned
20333 * return {String} The cleaned HTML
20335 cleanHtml : function(html){
20336 html = String(html);
20337 if(html.length > 5){
20338 if(Roo.isSafari){ // strip safari nonsense
20339 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20342 if(html == ' '){
20349 * HTML Editor -> Textarea
20350 * Protected method that will not generally be called directly. Syncs the contents
20351 * of the editor iframe with the textarea.
20353 syncValue : function(){
20354 if(this.initialized){
20355 var bd = (this.doc.body || this.doc.documentElement);
20356 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20357 var html = bd.innerHTML;
20359 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20360 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20362 html = '<div style="'+m[0]+'">' + html + '</div>';
20365 html = this.cleanHtml(html);
20366 // fix up the special chars.. normaly like back quotes in word...
20367 // however we do not want to do this with chinese..
20368 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20369 var cc = b.charCodeAt();
20371 (cc >= 0x4E00 && cc < 0xA000 ) ||
20372 (cc >= 0x3400 && cc < 0x4E00 ) ||
20373 (cc >= 0xf900 && cc < 0xfb00 )
20379 if(this.owner.fireEvent('beforesync', this, html) !== false){
20380 this.el.dom.value = html;
20381 this.owner.fireEvent('sync', this, html);
20387 * Protected method that will not generally be called directly. Pushes the value of the textarea
20388 * into the iframe editor.
20390 pushValue : function(){
20391 if(this.initialized){
20392 var v = this.el.dom.value.trim();
20394 // if(v.length < 1){
20398 if(this.owner.fireEvent('beforepush', this, v) !== false){
20399 var d = (this.doc.body || this.doc.documentElement);
20401 this.cleanUpPaste();
20402 this.el.dom.value = d.innerHTML;
20403 this.owner.fireEvent('push', this, v);
20409 deferFocus : function(){
20410 this.focus.defer(10, this);
20414 focus : function(){
20415 if(this.win && !this.sourceEditMode){
20422 assignDocWin: function()
20424 var iframe = this.iframe;
20427 this.doc = iframe.contentWindow.document;
20428 this.win = iframe.contentWindow;
20430 // if (!Roo.get(this.frameId)) {
20433 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20434 // this.win = Roo.get(this.frameId).dom.contentWindow;
20436 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20440 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20441 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20446 initEditor : function(){
20447 //console.log("INIT EDITOR");
20448 this.assignDocWin();
20452 this.doc.designMode="on";
20454 this.doc.write(this.getDocMarkup());
20457 var dbody = (this.doc.body || this.doc.documentElement);
20458 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20459 // this copies styles from the containing element into thsi one..
20460 // not sure why we need all of this..
20461 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20463 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20464 //ss['background-attachment'] = 'fixed'; // w3c
20465 dbody.bgProperties = 'fixed'; // ie
20466 //Roo.DomHelper.applyStyles(dbody, ss);
20467 Roo.EventManager.on(this.doc, {
20468 //'mousedown': this.onEditorEvent,
20469 'mouseup': this.onEditorEvent,
20470 'dblclick': this.onEditorEvent,
20471 'click': this.onEditorEvent,
20472 'keyup': this.onEditorEvent,
20477 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20479 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20480 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20482 this.initialized = true;
20484 this.owner.fireEvent('initialize', this);
20489 onDestroy : function(){
20495 //for (var i =0; i < this.toolbars.length;i++) {
20496 // // fixme - ask toolbars for heights?
20497 // this.toolbars[i].onDestroy();
20500 //this.wrap.dom.innerHTML = '';
20501 //this.wrap.remove();
20506 onFirstFocus : function(){
20508 this.assignDocWin();
20511 this.activated = true;
20514 if(Roo.isGecko){ // prevent silly gecko errors
20516 var s = this.win.getSelection();
20517 if(!s.focusNode || s.focusNode.nodeType != 3){
20518 var r = s.getRangeAt(0);
20519 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20524 this.execCmd('useCSS', true);
20525 this.execCmd('styleWithCSS', false);
20528 this.owner.fireEvent('activate', this);
20532 adjustFont: function(btn){
20533 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20534 //if(Roo.isSafari){ // safari
20537 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20538 if(Roo.isSafari){ // safari
20539 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20540 v = (v < 10) ? 10 : v;
20541 v = (v > 48) ? 48 : v;
20542 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20547 v = Math.max(1, v+adjust);
20549 this.execCmd('FontSize', v );
20552 onEditorEvent : function(e)
20554 this.owner.fireEvent('editorevent', this, e);
20555 // this.updateToolbar();
20556 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20559 insertTag : function(tg)
20561 // could be a bit smarter... -> wrap the current selected tRoo..
20562 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20564 range = this.createRange(this.getSelection());
20565 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20566 wrappingNode.appendChild(range.extractContents());
20567 range.insertNode(wrappingNode);
20574 this.execCmd("formatblock", tg);
20578 insertText : function(txt)
20582 var range = this.createRange();
20583 range.deleteContents();
20584 //alert(Sender.getAttribute('label'));
20586 range.insertNode(this.doc.createTextNode(txt));
20592 * Executes a Midas editor command on the editor document and performs necessary focus and
20593 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20594 * @param {String} cmd The Midas command
20595 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20597 relayCmd : function(cmd, value){
20599 this.execCmd(cmd, value);
20600 this.owner.fireEvent('editorevent', this);
20601 //this.updateToolbar();
20602 this.owner.deferFocus();
20606 * Executes a Midas editor command directly on the editor document.
20607 * For visual commands, you should use {@link #relayCmd} instead.
20608 * <b>This should only be called after the editor is initialized.</b>
20609 * @param {String} cmd The Midas command
20610 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20612 execCmd : function(cmd, value){
20613 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20620 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20622 * @param {String} text | dom node..
20624 insertAtCursor : function(text)
20629 if(!this.activated){
20635 var r = this.doc.selection.createRange();
20646 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20650 // from jquery ui (MIT licenced)
20652 var win = this.win;
20654 if (win.getSelection && win.getSelection().getRangeAt) {
20655 range = win.getSelection().getRangeAt(0);
20656 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20657 range.insertNode(node);
20658 } else if (win.document.selection && win.document.selection.createRange) {
20659 // no firefox support
20660 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20661 win.document.selection.createRange().pasteHTML(txt);
20663 // no firefox support
20664 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20665 this.execCmd('InsertHTML', txt);
20674 mozKeyPress : function(e){
20676 var c = e.getCharCode(), cmd;
20679 c = String.fromCharCode(c).toLowerCase();
20693 this.cleanUpPaste.defer(100, this);
20701 e.preventDefault();
20709 fixKeys : function(){ // load time branching for fastest keydown performance
20711 return function(e){
20712 var k = e.getKey(), r;
20715 r = this.doc.selection.createRange();
20718 r.pasteHTML('    ');
20725 r = this.doc.selection.createRange();
20727 var target = r.parentElement();
20728 if(!target || target.tagName.toLowerCase() != 'li'){
20730 r.pasteHTML('<br />');
20736 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20737 this.cleanUpPaste.defer(100, this);
20743 }else if(Roo.isOpera){
20744 return function(e){
20745 var k = e.getKey();
20749 this.execCmd('InsertHTML','    ');
20752 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20753 this.cleanUpPaste.defer(100, this);
20758 }else if(Roo.isSafari){
20759 return function(e){
20760 var k = e.getKey();
20764 this.execCmd('InsertText','\t');
20768 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20769 this.cleanUpPaste.defer(100, this);
20777 getAllAncestors: function()
20779 var p = this.getSelectedNode();
20782 a.push(p); // push blank onto stack..
20783 p = this.getParentElement();
20787 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20791 a.push(this.doc.body);
20795 lastSelNode : false,
20798 getSelection : function()
20800 this.assignDocWin();
20801 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20804 getSelectedNode: function()
20806 // this may only work on Gecko!!!
20808 // should we cache this!!!!
20813 var range = this.createRange(this.getSelection()).cloneRange();
20816 var parent = range.parentElement();
20818 var testRange = range.duplicate();
20819 testRange.moveToElementText(parent);
20820 if (testRange.inRange(range)) {
20823 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20826 parent = parent.parentElement;
20831 // is ancestor a text element.
20832 var ac = range.commonAncestorContainer;
20833 if (ac.nodeType == 3) {
20834 ac = ac.parentNode;
20837 var ar = ac.childNodes;
20840 var other_nodes = [];
20841 var has_other_nodes = false;
20842 for (var i=0;i<ar.length;i++) {
20843 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20846 // fullly contained node.
20848 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20853 // probably selected..
20854 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20855 other_nodes.push(ar[i]);
20859 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20864 has_other_nodes = true;
20866 if (!nodes.length && other_nodes.length) {
20867 nodes= other_nodes;
20869 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20875 createRange: function(sel)
20877 // this has strange effects when using with
20878 // top toolbar - not sure if it's a great idea.
20879 //this.editor.contentWindow.focus();
20880 if (typeof sel != "undefined") {
20882 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20884 return this.doc.createRange();
20887 return this.doc.createRange();
20890 getParentElement: function()
20893 this.assignDocWin();
20894 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20896 var range = this.createRange(sel);
20899 var p = range.commonAncestorContainer;
20900 while (p.nodeType == 3) { // text node
20911 * Range intersection.. the hard stuff...
20915 * [ -- selected range --- ]
20919 * if end is before start or hits it. fail.
20920 * if start is after end or hits it fail.
20922 * if either hits (but other is outside. - then it's not
20928 // @see http://www.thismuchiknow.co.uk/?p=64.
20929 rangeIntersectsNode : function(range, node)
20931 var nodeRange = node.ownerDocument.createRange();
20933 nodeRange.selectNode(node);
20935 nodeRange.selectNodeContents(node);
20938 var rangeStartRange = range.cloneRange();
20939 rangeStartRange.collapse(true);
20941 var rangeEndRange = range.cloneRange();
20942 rangeEndRange.collapse(false);
20944 var nodeStartRange = nodeRange.cloneRange();
20945 nodeStartRange.collapse(true);
20947 var nodeEndRange = nodeRange.cloneRange();
20948 nodeEndRange.collapse(false);
20950 return rangeStartRange.compareBoundaryPoints(
20951 Range.START_TO_START, nodeEndRange) == -1 &&
20952 rangeEndRange.compareBoundaryPoints(
20953 Range.START_TO_START, nodeStartRange) == 1;
20957 rangeCompareNode : function(range, node)
20959 var nodeRange = node.ownerDocument.createRange();
20961 nodeRange.selectNode(node);
20963 nodeRange.selectNodeContents(node);
20967 range.collapse(true);
20969 nodeRange.collapse(true);
20971 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20972 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20974 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20976 var nodeIsBefore = ss == 1;
20977 var nodeIsAfter = ee == -1;
20979 if (nodeIsBefore && nodeIsAfter) {
20982 if (!nodeIsBefore && nodeIsAfter) {
20983 return 1; //right trailed.
20986 if (nodeIsBefore && !nodeIsAfter) {
20987 return 2; // left trailed.
20993 // private? - in a new class?
20994 cleanUpPaste : function()
20996 // cleans up the whole document..
20997 Roo.log('cleanuppaste');
20999 this.cleanUpChildren(this.doc.body);
21000 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21001 if (clean != this.doc.body.innerHTML) {
21002 this.doc.body.innerHTML = clean;
21007 cleanWordChars : function(input) {// change the chars to hex code
21008 var he = Roo.HtmlEditorCore;
21010 var output = input;
21011 Roo.each(he.swapCodes, function(sw) {
21012 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21014 output = output.replace(swapper, sw[1]);
21021 cleanUpChildren : function (n)
21023 if (!n.childNodes.length) {
21026 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21027 this.cleanUpChild(n.childNodes[i]);
21034 cleanUpChild : function (node)
21037 //console.log(node);
21038 if (node.nodeName == "#text") {
21039 // clean up silly Windows -- stuff?
21042 if (node.nodeName == "#comment") {
21043 node.parentNode.removeChild(node);
21044 // clean up silly Windows -- stuff?
21047 var lcname = node.tagName.toLowerCase();
21048 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21049 // whitelist of tags..
21051 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21053 node.parentNode.removeChild(node);
21058 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21060 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21061 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21063 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21064 // remove_keep_children = true;
21067 if (remove_keep_children) {
21068 this.cleanUpChildren(node);
21069 // inserts everything just before this node...
21070 while (node.childNodes.length) {
21071 var cn = node.childNodes[0];
21072 node.removeChild(cn);
21073 node.parentNode.insertBefore(cn, node);
21075 node.parentNode.removeChild(node);
21079 if (!node.attributes || !node.attributes.length) {
21080 this.cleanUpChildren(node);
21084 function cleanAttr(n,v)
21087 if (v.match(/^\./) || v.match(/^\//)) {
21090 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21093 if (v.match(/^#/)) {
21096 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21097 node.removeAttribute(n);
21101 var cwhite = this.cwhite;
21102 var cblack = this.cblack;
21104 function cleanStyle(n,v)
21106 if (v.match(/expression/)) { //XSS?? should we even bother..
21107 node.removeAttribute(n);
21111 var parts = v.split(/;/);
21114 Roo.each(parts, function(p) {
21115 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21119 var l = p.split(':').shift().replace(/\s+/g,'');
21120 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21122 if ( cwhite.length && cblack.indexOf(l) > -1) {
21123 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21124 //node.removeAttribute(n);
21128 // only allow 'c whitelisted system attributes'
21129 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21130 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21131 //node.removeAttribute(n);
21141 if (clean.length) {
21142 node.setAttribute(n, clean.join(';'));
21144 node.removeAttribute(n);
21150 for (var i = node.attributes.length-1; i > -1 ; i--) {
21151 var a = node.attributes[i];
21154 if (a.name.toLowerCase().substr(0,2)=='on') {
21155 node.removeAttribute(a.name);
21158 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21159 node.removeAttribute(a.name);
21162 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21163 cleanAttr(a.name,a.value); // fixme..
21166 if (a.name == 'style') {
21167 cleanStyle(a.name,a.value);
21170 /// clean up MS crap..
21171 // tecnically this should be a list of valid class'es..
21174 if (a.name == 'class') {
21175 if (a.value.match(/^Mso/)) {
21176 node.className = '';
21179 if (a.value.match(/body/)) {
21180 node.className = '';
21191 this.cleanUpChildren(node);
21197 * Clean up MS wordisms...
21199 cleanWord : function(node)
21204 this.cleanWord(this.doc.body);
21207 if (node.nodeName == "#text") {
21208 // clean up silly Windows -- stuff?
21211 if (node.nodeName == "#comment") {
21212 node.parentNode.removeChild(node);
21213 // clean up silly Windows -- stuff?
21217 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21218 node.parentNode.removeChild(node);
21222 // remove - but keep children..
21223 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21224 while (node.childNodes.length) {
21225 var cn = node.childNodes[0];
21226 node.removeChild(cn);
21227 node.parentNode.insertBefore(cn, node);
21229 node.parentNode.removeChild(node);
21230 this.iterateChildren(node, this.cleanWord);
21234 if (node.className.length) {
21236 var cn = node.className.split(/\W+/);
21238 Roo.each(cn, function(cls) {
21239 if (cls.match(/Mso[a-zA-Z]+/)) {
21244 node.className = cna.length ? cna.join(' ') : '';
21246 node.removeAttribute("class");
21250 if (node.hasAttribute("lang")) {
21251 node.removeAttribute("lang");
21254 if (node.hasAttribute("style")) {
21256 var styles = node.getAttribute("style").split(";");
21258 Roo.each(styles, function(s) {
21259 if (!s.match(/:/)) {
21262 var kv = s.split(":");
21263 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21266 // what ever is left... we allow.
21269 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21270 if (!nstyle.length) {
21271 node.removeAttribute('style');
21274 this.iterateChildren(node, this.cleanWord);
21280 * iterateChildren of a Node, calling fn each time, using this as the scole..
21281 * @param {DomNode} node node to iterate children of.
21282 * @param {Function} fn method of this class to call on each item.
21284 iterateChildren : function(node, fn)
21286 if (!node.childNodes.length) {
21289 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21290 fn.call(this, node.childNodes[i])
21296 * cleanTableWidths.
21298 * Quite often pasting from word etc.. results in tables with column and widths.
21299 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21302 cleanTableWidths : function(node)
21307 this.cleanTableWidths(this.doc.body);
21312 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21315 Roo.log(node.tagName);
21316 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21317 this.iterateChildren(node, this.cleanTableWidths);
21320 if (node.hasAttribute('width')) {
21321 node.removeAttribute('width');
21325 if (node.hasAttribute("style")) {
21328 var styles = node.getAttribute("style").split(";");
21330 Roo.each(styles, function(s) {
21331 if (!s.match(/:/)) {
21334 var kv = s.split(":");
21335 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21338 // what ever is left... we allow.
21341 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21342 if (!nstyle.length) {
21343 node.removeAttribute('style');
21347 this.iterateChildren(node, this.cleanTableWidths);
21355 domToHTML : function(currentElement, depth, nopadtext) {
21357 depth = depth || 0;
21358 nopadtext = nopadtext || false;
21360 if (!currentElement) {
21361 return this.domToHTML(this.doc.body);
21364 //Roo.log(currentElement);
21366 var allText = false;
21367 var nodeName = currentElement.nodeName;
21368 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21370 if (nodeName == '#text') {
21372 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21377 if (nodeName != 'BODY') {
21380 // Prints the node tagName, such as <A>, <IMG>, etc
21383 for(i = 0; i < currentElement.attributes.length;i++) {
21385 var aname = currentElement.attributes.item(i).name;
21386 if (!currentElement.attributes.item(i).value.length) {
21389 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21392 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21401 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21404 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21409 // Traverse the tree
21411 var currentElementChild = currentElement.childNodes.item(i);
21412 var allText = true;
21413 var innerHTML = '';
21415 while (currentElementChild) {
21416 // Formatting code (indent the tree so it looks nice on the screen)
21417 var nopad = nopadtext;
21418 if (lastnode == 'SPAN') {
21422 if (currentElementChild.nodeName == '#text') {
21423 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21424 toadd = nopadtext ? toadd : toadd.trim();
21425 if (!nopad && toadd.length > 80) {
21426 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21428 innerHTML += toadd;
21431 currentElementChild = currentElement.childNodes.item(i);
21437 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21439 // Recursively traverse the tree structure of the child node
21440 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21441 lastnode = currentElementChild.nodeName;
21443 currentElementChild=currentElement.childNodes.item(i);
21449 // The remaining code is mostly for formatting the tree
21450 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21455 ret+= "</"+tagName+">";
21461 applyBlacklists : function()
21463 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21464 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21468 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21469 if (b.indexOf(tag) > -1) {
21472 this.white.push(tag);
21476 Roo.each(w, function(tag) {
21477 if (b.indexOf(tag) > -1) {
21480 if (this.white.indexOf(tag) > -1) {
21483 this.white.push(tag);
21488 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21489 if (w.indexOf(tag) > -1) {
21492 this.black.push(tag);
21496 Roo.each(b, function(tag) {
21497 if (w.indexOf(tag) > -1) {
21500 if (this.black.indexOf(tag) > -1) {
21503 this.black.push(tag);
21508 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21509 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21513 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21514 if (b.indexOf(tag) > -1) {
21517 this.cwhite.push(tag);
21521 Roo.each(w, function(tag) {
21522 if (b.indexOf(tag) > -1) {
21525 if (this.cwhite.indexOf(tag) > -1) {
21528 this.cwhite.push(tag);
21533 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21534 if (w.indexOf(tag) > -1) {
21537 this.cblack.push(tag);
21541 Roo.each(b, function(tag) {
21542 if (w.indexOf(tag) > -1) {
21545 if (this.cblack.indexOf(tag) > -1) {
21548 this.cblack.push(tag);
21553 setStylesheets : function(stylesheets)
21555 if(typeof(stylesheets) == 'string'){
21556 Roo.get(this.iframe.contentDocument.head).createChild({
21558 rel : 'stylesheet',
21567 Roo.each(stylesheets, function(s) {
21572 Roo.get(_this.iframe.contentDocument.head).createChild({
21574 rel : 'stylesheet',
21583 removeStylesheets : function()
21587 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21592 // hide stuff that is not compatible
21606 * @event specialkey
21610 * @cfg {String} fieldClass @hide
21613 * @cfg {String} focusClass @hide
21616 * @cfg {String} autoCreate @hide
21619 * @cfg {String} inputType @hide
21622 * @cfg {String} invalidClass @hide
21625 * @cfg {String} invalidText @hide
21628 * @cfg {String} msgFx @hide
21631 * @cfg {String} validateOnBlur @hide
21635 Roo.HtmlEditorCore.white = [
21636 'area', 'br', 'img', 'input', 'hr', 'wbr',
21638 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21639 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21640 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21641 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21642 'table', 'ul', 'xmp',
21644 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21647 'dir', 'menu', 'ol', 'ul', 'dl',
21653 Roo.HtmlEditorCore.black = [
21654 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21656 'base', 'basefont', 'bgsound', 'blink', 'body',
21657 'frame', 'frameset', 'head', 'html', 'ilayer',
21658 'iframe', 'layer', 'link', 'meta', 'object',
21659 'script', 'style' ,'title', 'xml' // clean later..
21661 Roo.HtmlEditorCore.clean = [
21662 'script', 'style', 'title', 'xml'
21664 Roo.HtmlEditorCore.remove = [
21669 Roo.HtmlEditorCore.ablack = [
21673 Roo.HtmlEditorCore.aclean = [
21674 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21678 Roo.HtmlEditorCore.pwhite= [
21679 'http', 'https', 'mailto'
21682 // white listed style attributes.
21683 Roo.HtmlEditorCore.cwhite= [
21684 // 'text-align', /// default is to allow most things..
21690 // black listed style attributes.
21691 Roo.HtmlEditorCore.cblack= [
21692 // 'font-size' -- this can be set by the project
21696 Roo.HtmlEditorCore.swapCodes =[
21715 * @class Roo.bootstrap.HtmlEditor
21716 * @extends Roo.bootstrap.TextArea
21717 * Bootstrap HtmlEditor class
21720 * Create a new HtmlEditor
21721 * @param {Object} config The config object
21724 Roo.bootstrap.HtmlEditor = function(config){
21725 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21726 if (!this.toolbars) {
21727 this.toolbars = [];
21729 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21732 * @event initialize
21733 * Fires when the editor is fully initialized (including the iframe)
21734 * @param {HtmlEditor} this
21739 * Fires when the editor is first receives the focus. Any insertion must wait
21740 * until after this event.
21741 * @param {HtmlEditor} this
21745 * @event beforesync
21746 * Fires before the textarea is updated with content from the editor iframe. Return false
21747 * to cancel the sync.
21748 * @param {HtmlEditor} this
21749 * @param {String} html
21753 * @event beforepush
21754 * Fires before the iframe editor is updated with content from the textarea. Return false
21755 * to cancel the push.
21756 * @param {HtmlEditor} this
21757 * @param {String} html
21762 * Fires when the textarea is updated with content from the editor iframe.
21763 * @param {HtmlEditor} this
21764 * @param {String} html
21769 * Fires when the iframe editor is updated with content from the textarea.
21770 * @param {HtmlEditor} this
21771 * @param {String} html
21775 * @event editmodechange
21776 * Fires when the editor switches edit modes
21777 * @param {HtmlEditor} this
21778 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21780 editmodechange: true,
21782 * @event editorevent
21783 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21784 * @param {HtmlEditor} this
21788 * @event firstfocus
21789 * Fires when on first focus - needed by toolbars..
21790 * @param {HtmlEditor} this
21795 * Auto save the htmlEditor value as a file into Events
21796 * @param {HtmlEditor} this
21800 * @event savedpreview
21801 * preview the saved version of htmlEditor
21802 * @param {HtmlEditor} this
21809 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21813 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21818 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21823 * @cfg {Number} height (in pixels)
21827 * @cfg {Number} width (in pixels)
21832 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21835 stylesheets: false,
21840 // private properties
21841 validationEvent : false,
21843 initialized : false,
21846 onFocus : Roo.emptyFn,
21848 hideMode:'offsets',
21851 tbContainer : false,
21853 toolbarContainer :function() {
21854 return this.wrap.select('.x-html-editor-tb',true).first();
21858 * Protected method that will not generally be called directly. It
21859 * is called when the editor creates its toolbar. Override this method if you need to
21860 * add custom toolbar buttons.
21861 * @param {HtmlEditor} editor
21863 createToolbar : function(){
21865 Roo.log("create toolbars");
21867 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21868 this.toolbars[0].render(this.toolbarContainer());
21872 // if (!editor.toolbars || !editor.toolbars.length) {
21873 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21876 // for (var i =0 ; i < editor.toolbars.length;i++) {
21877 // editor.toolbars[i] = Roo.factory(
21878 // typeof(editor.toolbars[i]) == 'string' ?
21879 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21880 // Roo.bootstrap.HtmlEditor);
21881 // editor.toolbars[i].init(editor);
21887 onRender : function(ct, position)
21889 // Roo.log("Call onRender: " + this.xtype);
21891 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21893 this.wrap = this.inputEl().wrap({
21894 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21897 this.editorcore.onRender(ct, position);
21899 if (this.resizable) {
21900 this.resizeEl = new Roo.Resizable(this.wrap, {
21904 minHeight : this.height,
21905 height: this.height,
21906 handles : this.resizable,
21909 resize : function(r, w, h) {
21910 _t.onResize(w,h); // -something
21916 this.createToolbar(this);
21919 if(!this.width && this.resizable){
21920 this.setSize(this.wrap.getSize());
21922 if (this.resizeEl) {
21923 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21924 // should trigger onReize..
21930 onResize : function(w, h)
21932 Roo.log('resize: ' +w + ',' + h );
21933 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21937 if(this.inputEl() ){
21938 if(typeof w == 'number'){
21939 var aw = w - this.wrap.getFrameWidth('lr');
21940 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21943 if(typeof h == 'number'){
21944 var tbh = -11; // fixme it needs to tool bar size!
21945 for (var i =0; i < this.toolbars.length;i++) {
21946 // fixme - ask toolbars for heights?
21947 tbh += this.toolbars[i].el.getHeight();
21948 //if (this.toolbars[i].footer) {
21949 // tbh += this.toolbars[i].footer.el.getHeight();
21957 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21958 ah -= 5; // knock a few pixes off for look..
21959 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21963 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21964 this.editorcore.onResize(ew,eh);
21969 * Toggles the editor between standard and source edit mode.
21970 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21972 toggleSourceEdit : function(sourceEditMode)
21974 this.editorcore.toggleSourceEdit(sourceEditMode);
21976 if(this.editorcore.sourceEditMode){
21977 Roo.log('editor - showing textarea');
21980 // Roo.log(this.syncValue());
21982 this.inputEl().removeClass(['hide', 'x-hidden']);
21983 this.inputEl().dom.removeAttribute('tabIndex');
21984 this.inputEl().focus();
21986 Roo.log('editor - hiding textarea');
21988 // Roo.log(this.pushValue());
21991 this.inputEl().addClass(['hide', 'x-hidden']);
21992 this.inputEl().dom.setAttribute('tabIndex', -1);
21993 //this.deferFocus();
21996 if(this.resizable){
21997 this.setSize(this.wrap.getSize());
22000 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22003 // private (for BoxComponent)
22004 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22006 // private (for BoxComponent)
22007 getResizeEl : function(){
22011 // private (for BoxComponent)
22012 getPositionEl : function(){
22017 initEvents : function(){
22018 this.originalValue = this.getValue();
22022 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22025 // markInvalid : Roo.emptyFn,
22027 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22030 // clearInvalid : Roo.emptyFn,
22032 setValue : function(v){
22033 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22034 this.editorcore.pushValue();
22039 deferFocus : function(){
22040 this.focus.defer(10, this);
22044 focus : function(){
22045 this.editorcore.focus();
22051 onDestroy : function(){
22057 for (var i =0; i < this.toolbars.length;i++) {
22058 // fixme - ask toolbars for heights?
22059 this.toolbars[i].onDestroy();
22062 this.wrap.dom.innerHTML = '';
22063 this.wrap.remove();
22068 onFirstFocus : function(){
22069 //Roo.log("onFirstFocus");
22070 this.editorcore.onFirstFocus();
22071 for (var i =0; i < this.toolbars.length;i++) {
22072 this.toolbars[i].onFirstFocus();
22078 syncValue : function()
22080 this.editorcore.syncValue();
22083 pushValue : function()
22085 this.editorcore.pushValue();
22089 // hide stuff that is not compatible
22103 * @event specialkey
22107 * @cfg {String} fieldClass @hide
22110 * @cfg {String} focusClass @hide
22113 * @cfg {String} autoCreate @hide
22116 * @cfg {String} inputType @hide
22119 * @cfg {String} invalidClass @hide
22122 * @cfg {String} invalidText @hide
22125 * @cfg {String} msgFx @hide
22128 * @cfg {String} validateOnBlur @hide
22137 Roo.namespace('Roo.bootstrap.htmleditor');
22139 * @class Roo.bootstrap.HtmlEditorToolbar1
22144 new Roo.bootstrap.HtmlEditor({
22147 new Roo.bootstrap.HtmlEditorToolbar1({
22148 disable : { fonts: 1 , format: 1, ..., ... , ...],
22154 * @cfg {Object} disable List of elements to disable..
22155 * @cfg {Array} btns List of additional buttons.
22159 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22162 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22165 Roo.apply(this, config);
22167 // default disabled, based on 'good practice'..
22168 this.disable = this.disable || {};
22169 Roo.applyIf(this.disable, {
22172 specialElements : true
22174 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22176 this.editor = config.editor;
22177 this.editorcore = config.editor.editorcore;
22179 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22181 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22182 // dont call parent... till later.
22184 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
22189 editorcore : false,
22194 "h1","h2","h3","h4","h5","h6",
22196 "abbr", "acronym", "address", "cite", "samp", "var",
22200 onRender : function(ct, position)
22202 // Roo.log("Call onRender: " + this.xtype);
22204 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22206 this.el.dom.style.marginBottom = '0';
22208 var editorcore = this.editorcore;
22209 var editor= this.editor;
22212 var btn = function(id,cmd , toggle, handler){
22214 var event = toggle ? 'toggle' : 'click';
22219 xns: Roo.bootstrap,
22222 enableToggle:toggle !== false,
22224 pressed : toggle ? false : null,
22227 a.listeners[toggle ? 'toggle' : 'click'] = function() {
22228 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
22237 xns: Roo.bootstrap,
22238 glyphicon : 'font',
22242 xns: Roo.bootstrap,
22246 Roo.each(this.formats, function(f) {
22247 style.menu.items.push({
22249 xns: Roo.bootstrap,
22250 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22255 editorcore.insertTag(this.tagname);
22262 children.push(style);
22265 btn('bold',false,true);
22266 btn('italic',false,true);
22267 btn('align-left', 'justifyleft',true);
22268 btn('align-center', 'justifycenter',true);
22269 btn('align-right' , 'justifyright',true);
22270 btn('link', false, false, function(btn) {
22271 //Roo.log("create link?");
22272 var url = prompt(this.createLinkText, this.defaultLinkValue);
22273 if(url && url != 'http:/'+'/'){
22274 this.editorcore.relayCmd('createlink', url);
22277 btn('list','insertunorderedlist',true);
22278 btn('pencil', false,true, function(btn){
22281 this.toggleSourceEdit(btn.pressed);
22287 xns: Roo.bootstrap,
22292 xns: Roo.bootstrap,
22297 cog.menu.items.push({
22299 xns: Roo.bootstrap,
22300 html : Clean styles,
22305 editorcore.insertTag(this.tagname);
22314 this.xtype = 'NavSimplebar';
22316 for(var i=0;i< children.length;i++) {
22318 this.buttons.add(this.addxtypeChild(children[i]));
22322 editor.on('editorevent', this.updateToolbar, this);
22324 onBtnClick : function(id)
22326 this.editorcore.relayCmd(id);
22327 this.editorcore.focus();
22331 * Protected method that will not generally be called directly. It triggers
22332 * a toolbar update by reading the markup state of the current selection in the editor.
22334 updateToolbar: function(){
22336 if(!this.editorcore.activated){
22337 this.editor.onFirstFocus(); // is this neeed?
22341 var btns = this.buttons;
22342 var doc = this.editorcore.doc;
22343 btns.get('bold').setActive(doc.queryCommandState('bold'));
22344 btns.get('italic').setActive(doc.queryCommandState('italic'));
22345 //btns.get('underline').setActive(doc.queryCommandState('underline'));
22347 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22348 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22349 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22351 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22352 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22355 var ans = this.editorcore.getAllAncestors();
22356 if (this.formatCombo) {
22359 var store = this.formatCombo.store;
22360 this.formatCombo.setValue("");
22361 for (var i =0; i < ans.length;i++) {
22362 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22364 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22372 // hides menus... - so this cant be on a menu...
22373 Roo.bootstrap.MenuMgr.hideAll();
22375 Roo.bootstrap.MenuMgr.hideAll();
22376 //this.editorsyncValue();
22378 onFirstFocus: function() {
22379 this.buttons.each(function(item){
22383 toggleSourceEdit : function(sourceEditMode){
22386 if(sourceEditMode){
22387 Roo.log("disabling buttons");
22388 this.buttons.each( function(item){
22389 if(item.cmd != 'pencil'){
22395 Roo.log("enabling buttons");
22396 if(this.editorcore.initialized){
22397 this.buttons.each( function(item){
22403 Roo.log("calling toggole on editor");
22404 // tell the editor that it's been pressed..
22405 this.editor.toggleSourceEdit(sourceEditMode);
22415 * @class Roo.bootstrap.Table.AbstractSelectionModel
22416 * @extends Roo.util.Observable
22417 * Abstract base class for grid SelectionModels. It provides the interface that should be
22418 * implemented by descendant classes. This class should not be directly instantiated.
22421 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22422 this.locked = false;
22423 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22427 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22428 /** @ignore Called by the grid automatically. Do not call directly. */
22429 init : function(grid){
22435 * Locks the selections.
22438 this.locked = true;
22442 * Unlocks the selections.
22444 unlock : function(){
22445 this.locked = false;
22449 * Returns true if the selections are locked.
22450 * @return {Boolean}
22452 isLocked : function(){
22453 return this.locked;
22457 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22458 * @class Roo.bootstrap.Table.RowSelectionModel
22459 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22460 * It supports multiple selections and keyboard selection/navigation.
22462 * @param {Object} config
22465 Roo.bootstrap.Table.RowSelectionModel = function(config){
22466 Roo.apply(this, config);
22467 this.selections = new Roo.util.MixedCollection(false, function(o){
22472 this.lastActive = false;
22476 * @event selectionchange
22477 * Fires when the selection changes
22478 * @param {SelectionModel} this
22480 "selectionchange" : true,
22482 * @event afterselectionchange
22483 * Fires after the selection changes (eg. by key press or clicking)
22484 * @param {SelectionModel} this
22486 "afterselectionchange" : true,
22488 * @event beforerowselect
22489 * Fires when a row is selected being selected, return false to cancel.
22490 * @param {SelectionModel} this
22491 * @param {Number} rowIndex The selected index
22492 * @param {Boolean} keepExisting False if other selections will be cleared
22494 "beforerowselect" : true,
22497 * Fires when a row is selected.
22498 * @param {SelectionModel} this
22499 * @param {Number} rowIndex The selected index
22500 * @param {Roo.data.Record} r The record
22502 "rowselect" : true,
22504 * @event rowdeselect
22505 * Fires when a row is deselected.
22506 * @param {SelectionModel} this
22507 * @param {Number} rowIndex The selected index
22509 "rowdeselect" : true
22511 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22512 this.locked = false;
22515 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22517 * @cfg {Boolean} singleSelect
22518 * True to allow selection of only one row at a time (defaults to false)
22520 singleSelect : false,
22523 initEvents : function()
22526 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22527 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
22528 //}else{ // allow click to work like normal
22529 // this.grid.on("rowclick", this.handleDragableRowClick, this);
22531 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22532 this.grid.on("rowclick", this.handleMouseDown, this);
22534 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22535 "up" : function(e){
22537 this.selectPrevious(e.shiftKey);
22538 }else if(this.last !== false && this.lastActive !== false){
22539 var last = this.last;
22540 this.selectRange(this.last, this.lastActive-1);
22541 this.grid.getView().focusRow(this.lastActive);
22542 if(last !== false){
22546 this.selectFirstRow();
22548 this.fireEvent("afterselectionchange", this);
22550 "down" : function(e){
22552 this.selectNext(e.shiftKey);
22553 }else if(this.last !== false && this.lastActive !== false){
22554 var last = this.last;
22555 this.selectRange(this.last, this.lastActive+1);
22556 this.grid.getView().focusRow(this.lastActive);
22557 if(last !== false){
22561 this.selectFirstRow();
22563 this.fireEvent("afterselectionchange", this);
22567 this.grid.store.on('load', function(){
22568 this.selections.clear();
22571 var view = this.grid.view;
22572 view.on("refresh", this.onRefresh, this);
22573 view.on("rowupdated", this.onRowUpdated, this);
22574 view.on("rowremoved", this.onRemove, this);
22579 onRefresh : function()
22581 var ds = this.grid.store, i, v = this.grid.view;
22582 var s = this.selections;
22583 s.each(function(r){
22584 if((i = ds.indexOfId(r.id)) != -1){
22593 onRemove : function(v, index, r){
22594 this.selections.remove(r);
22598 onRowUpdated : function(v, index, r){
22599 if(this.isSelected(r)){
22600 v.onRowSelect(index);
22606 * @param {Array} records The records to select
22607 * @param {Boolean} keepExisting (optional) True to keep existing selections
22609 selectRecords : function(records, keepExisting)
22612 this.clearSelections();
22614 var ds = this.grid.store;
22615 for(var i = 0, len = records.length; i < len; i++){
22616 this.selectRow(ds.indexOf(records[i]), true);
22621 * Gets the number of selected rows.
22624 getCount : function(){
22625 return this.selections.length;
22629 * Selects the first row in the grid.
22631 selectFirstRow : function(){
22636 * Select the last row.
22637 * @param {Boolean} keepExisting (optional) True to keep existing selections
22639 selectLastRow : function(keepExisting){
22640 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22641 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22645 * Selects the row immediately following the last selected row.
22646 * @param {Boolean} keepExisting (optional) True to keep existing selections
22648 selectNext : function(keepExisting)
22650 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22651 this.selectRow(this.last+1, keepExisting);
22652 this.grid.getView().focusRow(this.last);
22657 * Selects the row that precedes the last selected row.
22658 * @param {Boolean} keepExisting (optional) True to keep existing selections
22660 selectPrevious : function(keepExisting){
22662 this.selectRow(this.last-1, keepExisting);
22663 this.grid.getView().focusRow(this.last);
22668 * Returns the selected records
22669 * @return {Array} Array of selected records
22671 getSelections : function(){
22672 return [].concat(this.selections.items);
22676 * Returns the first selected record.
22679 getSelected : function(){
22680 return this.selections.itemAt(0);
22685 * Clears all selections.
22687 clearSelections : function(fast)
22693 var ds = this.grid.store;
22694 var s = this.selections;
22695 s.each(function(r){
22696 this.deselectRow(ds.indexOfId(r.id));
22700 this.selections.clear();
22707 * Selects all rows.
22709 selectAll : function(){
22713 this.selections.clear();
22714 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
22715 this.selectRow(i, true);
22720 * Returns True if there is a selection.
22721 * @return {Boolean}
22723 hasSelection : function(){
22724 return this.selections.length > 0;
22728 * Returns True if the specified row is selected.
22729 * @param {Number/Record} record The record or index of the record to check
22730 * @return {Boolean}
22732 isSelected : function(index){
22733 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
22734 return (r && this.selections.key(r.id) ? true : false);
22738 * Returns True if the specified record id is selected.
22739 * @param {String} id The id of record to check
22740 * @return {Boolean}
22742 isIdSelected : function(id){
22743 return (this.selections.key(id) ? true : false);
22748 handleMouseDBClick : function(e, t){
22752 handleMouseDown : function(e, t)
22754 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
22755 if(this.isLocked() || rowIndex < 0 ){
22758 if(e.shiftKey && this.last !== false){
22759 var last = this.last;
22760 this.selectRange(last, rowIndex, e.ctrlKey);
22761 this.last = last; // reset the last
22765 var isSelected = this.isSelected(rowIndex);
22766 //Roo.log("select row:" + rowIndex);
22768 this.deselectRow(rowIndex);
22770 this.selectRow(rowIndex, true);
22774 if(e.button !== 0 && isSelected){
22775 alert('rowIndex 2: ' + rowIndex);
22776 view.focusRow(rowIndex);
22777 }else if(e.ctrlKey && isSelected){
22778 this.deselectRow(rowIndex);
22779 }else if(!isSelected){
22780 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22781 view.focusRow(rowIndex);
22785 this.fireEvent("afterselectionchange", this);
22788 handleDragableRowClick : function(grid, rowIndex, e)
22790 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22791 this.selectRow(rowIndex, false);
22792 grid.view.focusRow(rowIndex);
22793 this.fireEvent("afterselectionchange", this);
22798 * Selects multiple rows.
22799 * @param {Array} rows Array of the indexes of the row to select
22800 * @param {Boolean} keepExisting (optional) True to keep existing selections
22802 selectRows : function(rows, keepExisting){
22804 this.clearSelections();
22806 for(var i = 0, len = rows.length; i < len; i++){
22807 this.selectRow(rows[i], true);
22812 * Selects a range of rows. All rows in between startRow and endRow are also selected.
22813 * @param {Number} startRow The index of the first row in the range
22814 * @param {Number} endRow The index of the last row in the range
22815 * @param {Boolean} keepExisting (optional) True to retain existing selections
22817 selectRange : function(startRow, endRow, keepExisting){
22822 this.clearSelections();
22824 if(startRow <= endRow){
22825 for(var i = startRow; i <= endRow; i++){
22826 this.selectRow(i, true);
22829 for(var i = startRow; i >= endRow; i--){
22830 this.selectRow(i, true);
22836 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22837 * @param {Number} startRow The index of the first row in the range
22838 * @param {Number} endRow The index of the last row in the range
22840 deselectRange : function(startRow, endRow, preventViewNotify){
22844 for(var i = startRow; i <= endRow; i++){
22845 this.deselectRow(i, preventViewNotify);
22851 * @param {Number} row The index of the row to select
22852 * @param {Boolean} keepExisting (optional) True to keep existing selections
22854 selectRow : function(index, keepExisting, preventViewNotify)
22856 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
22859 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22860 if(!keepExisting || this.singleSelect){
22861 this.clearSelections();
22864 var r = this.grid.store.getAt(index);
22865 //console.log('selectRow - record id :' + r.id);
22867 this.selections.add(r);
22868 this.last = this.lastActive = index;
22869 if(!preventViewNotify){
22870 var proxy = new Roo.Element(
22871 this.grid.getRowDom(index)
22873 proxy.addClass('bg-info info');
22875 this.fireEvent("rowselect", this, index, r);
22876 this.fireEvent("selectionchange", this);
22882 * @param {Number} row The index of the row to deselect
22884 deselectRow : function(index, preventViewNotify)
22889 if(this.last == index){
22892 if(this.lastActive == index){
22893 this.lastActive = false;
22896 var r = this.grid.store.getAt(index);
22901 this.selections.remove(r);
22902 //.console.log('deselectRow - record id :' + r.id);
22903 if(!preventViewNotify){
22905 var proxy = new Roo.Element(
22906 this.grid.getRowDom(index)
22908 proxy.removeClass('bg-info info');
22910 this.fireEvent("rowdeselect", this, index);
22911 this.fireEvent("selectionchange", this);
22915 restoreLast : function(){
22917 this.last = this._last;
22922 acceptsNav : function(row, col, cm){
22923 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22927 onEditorKey : function(field, e){
22928 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22933 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22935 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22937 }else if(k == e.ENTER && !e.ctrlKey){
22941 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22943 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22945 }else if(k == e.ESC){
22949 g.startEditing(newCell[0], newCell[1]);
22955 * Ext JS Library 1.1.1
22956 * Copyright(c) 2006-2007, Ext JS, LLC.
22958 * Originally Released Under LGPL - original licence link has changed is not relivant.
22961 * <script type="text/javascript">
22965 * @class Roo.bootstrap.PagingToolbar
22966 * @extends Roo.bootstrap.NavSimplebar
22967 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22969 * Create a new PagingToolbar
22970 * @param {Object} config The config object
22971 * @param {Roo.data.Store} store
22973 Roo.bootstrap.PagingToolbar = function(config)
22975 // old args format still supported... - xtype is prefered..
22976 // created from xtype...
22978 this.ds = config.dataSource;
22980 if (config.store && !this.ds) {
22981 this.store= Roo.factory(config.store, Roo.data);
22982 this.ds = this.store;
22983 this.ds.xmodule = this.xmodule || false;
22986 this.toolbarItems = [];
22987 if (config.items) {
22988 this.toolbarItems = config.items;
22991 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22996 this.bind(this.ds);
22999 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23003 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23005 * @cfg {Roo.data.Store} dataSource
23006 * The underlying data store providing the paged data
23009 * @cfg {String/HTMLElement/Element} container
23010 * container The id or element that will contain the toolbar
23013 * @cfg {Boolean} displayInfo
23014 * True to display the displayMsg (defaults to false)
23017 * @cfg {Number} pageSize
23018 * The number of records to display per page (defaults to 20)
23022 * @cfg {String} displayMsg
23023 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23025 displayMsg : 'Displaying {0} - {1} of {2}',
23027 * @cfg {String} emptyMsg
23028 * The message to display when no records are found (defaults to "No data to display")
23030 emptyMsg : 'No data to display',
23032 * Customizable piece of the default paging text (defaults to "Page")
23035 beforePageText : "Page",
23037 * Customizable piece of the default paging text (defaults to "of %0")
23040 afterPageText : "of {0}",
23042 * Customizable piece of the default paging text (defaults to "First Page")
23045 firstText : "First Page",
23047 * Customizable piece of the default paging text (defaults to "Previous Page")
23050 prevText : "Previous Page",
23052 * Customizable piece of the default paging text (defaults to "Next Page")
23055 nextText : "Next Page",
23057 * Customizable piece of the default paging text (defaults to "Last Page")
23060 lastText : "Last Page",
23062 * Customizable piece of the default paging text (defaults to "Refresh")
23065 refreshText : "Refresh",
23069 onRender : function(ct, position)
23071 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23072 this.navgroup.parentId = this.id;
23073 this.navgroup.onRender(this.el, null);
23074 // add the buttons to the navgroup
23076 if(this.displayInfo){
23077 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23078 this.displayEl = this.el.select('.x-paging-info', true).first();
23079 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23080 // this.displayEl = navel.el.select('span',true).first();
23086 Roo.each(_this.buttons, function(e){ // this might need to use render????
23087 Roo.factory(e).onRender(_this.el, null);
23091 Roo.each(_this.toolbarItems, function(e) {
23092 _this.navgroup.addItem(e);
23096 this.first = this.navgroup.addItem({
23097 tooltip: this.firstText,
23099 icon : 'fa fa-backward',
23101 preventDefault: true,
23102 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23105 this.prev = this.navgroup.addItem({
23106 tooltip: this.prevText,
23108 icon : 'fa fa-step-backward',
23110 preventDefault: true,
23111 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
23113 //this.addSeparator();
23116 var field = this.navgroup.addItem( {
23118 cls : 'x-paging-position',
23120 html : this.beforePageText +
23121 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23122 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
23125 this.field = field.el.select('input', true).first();
23126 this.field.on("keydown", this.onPagingKeydown, this);
23127 this.field.on("focus", function(){this.dom.select();});
23130 this.afterTextEl = field.el.select('.x-paging-after',true).first();
23131 //this.field.setHeight(18);
23132 //this.addSeparator();
23133 this.next = this.navgroup.addItem({
23134 tooltip: this.nextText,
23136 html : ' <i class="fa fa-step-forward">',
23138 preventDefault: true,
23139 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
23141 this.last = this.navgroup.addItem({
23142 tooltip: this.lastText,
23143 icon : 'fa fa-forward',
23146 preventDefault: true,
23147 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
23149 //this.addSeparator();
23150 this.loading = this.navgroup.addItem({
23151 tooltip: this.refreshText,
23152 icon: 'fa fa-refresh',
23153 preventDefault: true,
23154 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23160 updateInfo : function(){
23161 if(this.displayEl){
23162 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23163 var msg = count == 0 ?
23167 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
23169 this.displayEl.update(msg);
23174 onLoad : function(ds, r, o){
23175 this.cursor = o.params ? o.params.start : 0;
23176 var d = this.getPageData(),
23180 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23181 this.field.dom.value = ap;
23182 this.first.setDisabled(ap == 1);
23183 this.prev.setDisabled(ap == 1);
23184 this.next.setDisabled(ap == ps);
23185 this.last.setDisabled(ap == ps);
23186 this.loading.enable();
23191 getPageData : function(){
23192 var total = this.ds.getTotalCount();
23195 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23196 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23201 onLoadError : function(){
23202 this.loading.enable();
23206 onPagingKeydown : function(e){
23207 var k = e.getKey();
23208 var d = this.getPageData();
23210 var v = this.field.dom.value, pageNum;
23211 if(!v || isNaN(pageNum = parseInt(v, 10))){
23212 this.field.dom.value = d.activePage;
23215 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23216 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23219 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))
23221 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23222 this.field.dom.value = pageNum;
23223 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23226 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23228 var v = this.field.dom.value, pageNum;
23229 var increment = (e.shiftKey) ? 10 : 1;
23230 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23233 if(!v || isNaN(pageNum = parseInt(v, 10))) {
23234 this.field.dom.value = d.activePage;
23237 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23239 this.field.dom.value = parseInt(v, 10) + increment;
23240 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23241 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23248 beforeLoad : function(){
23250 this.loading.disable();
23255 onClick : function(which){
23264 ds.load({params:{start: 0, limit: this.pageSize}});
23267 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23270 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23273 var total = ds.getTotalCount();
23274 var extra = total % this.pageSize;
23275 var lastStart = extra ? (total - extra) : total-this.pageSize;
23276 ds.load({params:{start: lastStart, limit: this.pageSize}});
23279 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23285 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23286 * @param {Roo.data.Store} store The data store to unbind
23288 unbind : function(ds){
23289 ds.un("beforeload", this.beforeLoad, this);
23290 ds.un("load", this.onLoad, this);
23291 ds.un("loadexception", this.onLoadError, this);
23292 ds.un("remove", this.updateInfo, this);
23293 ds.un("add", this.updateInfo, this);
23294 this.ds = undefined;
23298 * Binds the paging toolbar to the specified {@link Roo.data.Store}
23299 * @param {Roo.data.Store} store The data store to bind
23301 bind : function(ds){
23302 ds.on("beforeload", this.beforeLoad, this);
23303 ds.on("load", this.onLoad, this);
23304 ds.on("loadexception", this.onLoadError, this);
23305 ds.on("remove", this.updateInfo, this);
23306 ds.on("add", this.updateInfo, this);
23317 * @class Roo.bootstrap.MessageBar
23318 * @extends Roo.bootstrap.Component
23319 * Bootstrap MessageBar class
23320 * @cfg {String} html contents of the MessageBar
23321 * @cfg {String} weight (info | success | warning | danger) default info
23322 * @cfg {String} beforeClass insert the bar before the given class
23323 * @cfg {Boolean} closable (true | false) default false
23324 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23327 * Create a new Element
23328 * @param {Object} config The config object
23331 Roo.bootstrap.MessageBar = function(config){
23332 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23335 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
23341 beforeClass: 'bootstrap-sticky-wrap',
23343 getAutoCreate : function(){
23347 cls: 'alert alert-dismissable alert-' + this.weight,
23352 html: this.html || ''
23358 cfg.cls += ' alert-messages-fixed';
23372 onRender : function(ct, position)
23374 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23377 var cfg = Roo.apply({}, this.getAutoCreate());
23381 cfg.cls += ' ' + this.cls;
23384 cfg.style = this.style;
23386 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23388 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23391 this.el.select('>button.close').on('click', this.hide, this);
23397 if (!this.rendered) {
23403 this.fireEvent('show', this);
23409 if (!this.rendered) {
23415 this.fireEvent('hide', this);
23418 update : function()
23420 // var e = this.el.dom.firstChild;
23422 // if(this.closable){
23423 // e = e.nextSibling;
23426 // e.data = this.html || '';
23428 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23444 * @class Roo.bootstrap.Graph
23445 * @extends Roo.bootstrap.Component
23446 * Bootstrap Graph class
23450 @cfg {String} graphtype bar | vbar | pie
23451 @cfg {number} g_x coodinator | centre x (pie)
23452 @cfg {number} g_y coodinator | centre y (pie)
23453 @cfg {number} g_r radius (pie)
23454 @cfg {number} g_height height of the chart (respected by all elements in the set)
23455 @cfg {number} g_width width of the chart (respected by all elements in the set)
23456 @cfg {Object} title The title of the chart
23459 -opts (object) options for the chart
23461 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23462 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23464 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.
23465 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23467 o stretch (boolean)
23469 -opts (object) options for the pie
23472 o startAngle (number)
23473 o endAngle (number)
23477 * Create a new Input
23478 * @param {Object} config The config object
23481 Roo.bootstrap.Graph = function(config){
23482 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23488 * The img click event for the img.
23489 * @param {Roo.EventObject} e
23495 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23506 //g_colors: this.colors,
23513 getAutoCreate : function(){
23524 onRender : function(ct,position){
23527 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23529 if (typeof(Raphael) == 'undefined') {
23530 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23534 this.raphael = Raphael(this.el.dom);
23536 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23537 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23538 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23539 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23541 r.text(160, 10, "Single Series Chart").attr(txtattr);
23542 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23543 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23544 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23546 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23547 r.barchart(330, 10, 300, 220, data1);
23548 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23549 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23552 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23553 // r.barchart(30, 30, 560, 250, xdata, {
23554 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23555 // axis : "0 0 1 1",
23556 // axisxlabels : xdata
23557 // //yvalues : cols,
23560 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23562 // this.load(null,xdata,{
23563 // axis : "0 0 1 1",
23564 // axisxlabels : xdata
23569 load : function(graphtype,xdata,opts)
23571 this.raphael.clear();
23573 graphtype = this.graphtype;
23578 var r = this.raphael,
23579 fin = function () {
23580 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23582 fout = function () {
23583 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23585 pfin = function() {
23586 this.sector.stop();
23587 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23590 this.label[0].stop();
23591 this.label[0].attr({ r: 7.5 });
23592 this.label[1].attr({ "font-weight": 800 });
23595 pfout = function() {
23596 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23599 this.label[0].animate({ r: 5 }, 500, "bounce");
23600 this.label[1].attr({ "font-weight": 400 });
23606 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23609 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23612 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23613 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23615 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23622 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23627 setTitle: function(o)
23632 initEvents: function() {
23635 this.el.on('click', this.onClick, this);
23639 onClick : function(e)
23641 Roo.log('img onclick');
23642 this.fireEvent('click', this, e);
23654 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23657 * @class Roo.bootstrap.dash.NumberBox
23658 * @extends Roo.bootstrap.Component
23659 * Bootstrap NumberBox class
23660 * @cfg {String} headline Box headline
23661 * @cfg {String} content Box content
23662 * @cfg {String} icon Box icon
23663 * @cfg {String} footer Footer text
23664 * @cfg {String} fhref Footer href
23667 * Create a new NumberBox
23668 * @param {Object} config The config object
23672 Roo.bootstrap.dash.NumberBox = function(config){
23673 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23677 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23686 getAutoCreate : function(){
23690 cls : 'small-box ',
23698 cls : 'roo-headline',
23699 html : this.headline
23703 cls : 'roo-content',
23704 html : this.content
23718 cls : 'ion ' + this.icon
23727 cls : 'small-box-footer',
23728 href : this.fhref || '#',
23732 cfg.cn.push(footer);
23739 onRender : function(ct,position){
23740 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23747 setHeadline: function (value)
23749 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23752 setFooter: function (value, href)
23754 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23757 this.el.select('a.small-box-footer',true).first().attr('href', href);
23762 setContent: function (value)
23764 this.el.select('.roo-content',true).first().dom.innerHTML = value;
23767 initEvents: function()
23781 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23784 * @class Roo.bootstrap.dash.TabBox
23785 * @extends Roo.bootstrap.Component
23786 * Bootstrap TabBox class
23787 * @cfg {String} title Title of the TabBox
23788 * @cfg {String} icon Icon of the TabBox
23789 * @cfg {Boolean} showtabs (true|false) show the tabs default true
23790 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23793 * Create a new TabBox
23794 * @param {Object} config The config object
23798 Roo.bootstrap.dash.TabBox = function(config){
23799 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23804 * When a pane is added
23805 * @param {Roo.bootstrap.dash.TabPane} pane
23809 * @event activatepane
23810 * When a pane is activated
23811 * @param {Roo.bootstrap.dash.TabPane} pane
23813 "activatepane" : true
23821 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
23826 tabScrollable : false,
23828 getChildContainer : function()
23830 return this.el.select('.tab-content', true).first();
23833 getAutoCreate : function(){
23837 cls: 'pull-left header',
23845 cls: 'fa ' + this.icon
23851 cls: 'nav nav-tabs pull-right',
23857 if(this.tabScrollable){
23864 cls: 'nav nav-tabs pull-right',
23875 cls: 'nav-tabs-custom',
23880 cls: 'tab-content no-padding',
23888 initEvents : function()
23890 //Roo.log('add add pane handler');
23891 this.on('addpane', this.onAddPane, this);
23894 * Updates the box title
23895 * @param {String} html to set the title to.
23897 setTitle : function(value)
23899 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23901 onAddPane : function(pane)
23903 this.panes.push(pane);
23904 //Roo.log('addpane');
23906 // tabs are rendere left to right..
23907 if(!this.showtabs){
23911 var ctr = this.el.select('.nav-tabs', true).first();
23914 var existing = ctr.select('.nav-tab',true);
23915 var qty = existing.getCount();;
23918 var tab = ctr.createChild({
23920 cls : 'nav-tab' + (qty ? '' : ' active'),
23928 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23931 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23933 pane.el.addClass('active');
23938 onTabClick : function(ev,un,ob,pane)
23940 //Roo.log('tab - prev default');
23941 ev.preventDefault();
23944 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23945 pane.tab.addClass('active');
23946 //Roo.log(pane.title);
23947 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23948 // technically we should have a deactivate event.. but maybe add later.
23949 // and it should not de-activate the selected tab...
23950 this.fireEvent('activatepane', pane);
23951 pane.el.addClass('active');
23952 pane.fireEvent('activate');
23957 getActivePane : function()
23960 Roo.each(this.panes, function(p) {
23961 if(p.el.hasClass('active')){
23982 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23984 * @class Roo.bootstrap.TabPane
23985 * @extends Roo.bootstrap.Component
23986 * Bootstrap TabPane class
23987 * @cfg {Boolean} active (false | true) Default false
23988 * @cfg {String} title title of panel
23992 * Create a new TabPane
23993 * @param {Object} config The config object
23996 Roo.bootstrap.dash.TabPane = function(config){
23997 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24003 * When a pane is activated
24004 * @param {Roo.bootstrap.dash.TabPane} pane
24011 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
24016 // the tabBox that this is attached to.
24019 getAutoCreate : function()
24027 cfg.cls += ' active';
24032 initEvents : function()
24034 //Roo.log('trigger add pane handler');
24035 this.parent().fireEvent('addpane', this)
24039 * Updates the tab title
24040 * @param {String} html to set the title to.
24042 setTitle: function(str)
24048 this.tab.select('a', true).first().dom.innerHTML = str;
24065 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24068 * @class Roo.bootstrap.menu.Menu
24069 * @extends Roo.bootstrap.Component
24070 * Bootstrap Menu class - container for Menu
24071 * @cfg {String} html Text of the menu
24072 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24073 * @cfg {String} icon Font awesome icon
24074 * @cfg {String} pos Menu align to (top | bottom) default bottom
24078 * Create a new Menu
24079 * @param {Object} config The config object
24083 Roo.bootstrap.menu.Menu = function(config){
24084 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24088 * @event beforeshow
24089 * Fires before this menu is displayed
24090 * @param {Roo.bootstrap.menu.Menu} this
24094 * @event beforehide
24095 * Fires before this menu is hidden
24096 * @param {Roo.bootstrap.menu.Menu} this
24101 * Fires after this menu is displayed
24102 * @param {Roo.bootstrap.menu.Menu} this
24107 * Fires after this menu is hidden
24108 * @param {Roo.bootstrap.menu.Menu} this
24113 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24114 * @param {Roo.bootstrap.menu.Menu} this
24115 * @param {Roo.EventObject} e
24122 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
24126 weight : 'default',
24131 getChildContainer : function() {
24132 if(this.isSubMenu){
24136 return this.el.select('ul.dropdown-menu', true).first();
24139 getAutoCreate : function()
24144 cls : 'roo-menu-text',
24152 cls : 'fa ' + this.icon
24163 cls : 'dropdown-button btn btn-' + this.weight,
24168 cls : 'dropdown-toggle btn btn-' + this.weight,
24178 cls : 'dropdown-menu'
24184 if(this.pos == 'top'){
24185 cfg.cls += ' dropup';
24188 if(this.isSubMenu){
24191 cls : 'dropdown-menu'
24198 onRender : function(ct, position)
24200 this.isSubMenu = ct.hasClass('dropdown-submenu');
24202 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24205 initEvents : function()
24207 if(this.isSubMenu){
24211 this.hidden = true;
24213 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24214 this.triggerEl.on('click', this.onTriggerPress, this);
24216 this.buttonEl = this.el.select('button.dropdown-button', true).first();
24217 this.buttonEl.on('click', this.onClick, this);
24223 if(this.isSubMenu){
24227 return this.el.select('ul.dropdown-menu', true).first();
24230 onClick : function(e)
24232 this.fireEvent("click", this, e);
24235 onTriggerPress : function(e)
24237 if (this.isVisible()) {
24244 isVisible : function(){
24245 return !this.hidden;
24250 this.fireEvent("beforeshow", this);
24252 this.hidden = false;
24253 this.el.addClass('open');
24255 Roo.get(document).on("mouseup", this.onMouseUp, this);
24257 this.fireEvent("show", this);
24264 this.fireEvent("beforehide", this);
24266 this.hidden = true;
24267 this.el.removeClass('open');
24269 Roo.get(document).un("mouseup", this.onMouseUp);
24271 this.fireEvent("hide", this);
24274 onMouseUp : function()
24288 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24291 * @class Roo.bootstrap.menu.Item
24292 * @extends Roo.bootstrap.Component
24293 * Bootstrap MenuItem class
24294 * @cfg {Boolean} submenu (true | false) default false
24295 * @cfg {String} html text of the item
24296 * @cfg {String} href the link
24297 * @cfg {Boolean} disable (true | false) default false
24298 * @cfg {Boolean} preventDefault (true | false) default true
24299 * @cfg {String} icon Font awesome icon
24300 * @cfg {String} pos Submenu align to (left | right) default right
24304 * Create a new Item
24305 * @param {Object} config The config object
24309 Roo.bootstrap.menu.Item = function(config){
24310 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24314 * Fires when the mouse is hovering over this menu
24315 * @param {Roo.bootstrap.menu.Item} this
24316 * @param {Roo.EventObject} e
24321 * Fires when the mouse exits this menu
24322 * @param {Roo.bootstrap.menu.Item} this
24323 * @param {Roo.EventObject} e
24329 * The raw click event for the entire grid.
24330 * @param {Roo.EventObject} e
24336 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
24341 preventDefault: true,
24346 getAutoCreate : function()
24351 cls : 'roo-menu-item-text',
24359 cls : 'fa ' + this.icon
24368 href : this.href || '#',
24375 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24379 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24381 if(this.pos == 'left'){
24382 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24389 initEvents : function()
24391 this.el.on('mouseover', this.onMouseOver, this);
24392 this.el.on('mouseout', this.onMouseOut, this);
24394 this.el.select('a', true).first().on('click', this.onClick, this);
24398 onClick : function(e)
24400 if(this.preventDefault){
24401 e.preventDefault();
24404 this.fireEvent("click", this, e);
24407 onMouseOver : function(e)
24409 if(this.submenu && this.pos == 'left'){
24410 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24413 this.fireEvent("mouseover", this, e);
24416 onMouseOut : function(e)
24418 this.fireEvent("mouseout", this, e);
24430 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24433 * @class Roo.bootstrap.menu.Separator
24434 * @extends Roo.bootstrap.Component
24435 * Bootstrap Separator class
24438 * Create a new Separator
24439 * @param {Object} config The config object
24443 Roo.bootstrap.menu.Separator = function(config){
24444 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24447 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24449 getAutoCreate : function(){
24470 * @class Roo.bootstrap.Tooltip
24471 * Bootstrap Tooltip class
24472 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24473 * to determine which dom element triggers the tooltip.
24475 * It needs to add support for additional attributes like tooltip-position
24478 * Create a new Toolti
24479 * @param {Object} config The config object
24482 Roo.bootstrap.Tooltip = function(config){
24483 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24486 Roo.apply(Roo.bootstrap.Tooltip, {
24488 * @function init initialize tooltip monitoring.
24492 currentTip : false,
24493 currentRegion : false,
24499 Roo.get(document).on('mouseover', this.enter ,this);
24500 Roo.get(document).on('mouseout', this.leave, this);
24503 this.currentTip = new Roo.bootstrap.Tooltip();
24506 enter : function(ev)
24508 var dom = ev.getTarget();
24510 //Roo.log(['enter',dom]);
24511 var el = Roo.fly(dom);
24512 if (this.currentEl) {
24514 //Roo.log(this.currentEl);
24515 //Roo.log(this.currentEl.contains(dom));
24516 if (this.currentEl == el) {
24519 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24525 if (this.currentTip.el) {
24526 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24530 if(!el || el.dom == document){
24536 // you can not look for children, as if el is the body.. then everythign is the child..
24537 if (!el.attr('tooltip')) { //
24538 if (!el.select("[tooltip]").elements.length) {
24541 // is the mouse over this child...?
24542 bindEl = el.select("[tooltip]").first();
24543 var xy = ev.getXY();
24544 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24545 //Roo.log("not in region.");
24548 //Roo.log("child element over..");
24551 this.currentEl = bindEl;
24552 this.currentTip.bind(bindEl);
24553 this.currentRegion = Roo.lib.Region.getRegion(dom);
24554 this.currentTip.enter();
24557 leave : function(ev)
24559 var dom = ev.getTarget();
24560 //Roo.log(['leave',dom]);
24561 if (!this.currentEl) {
24566 if (dom != this.currentEl.dom) {
24569 var xy = ev.getXY();
24570 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24573 // only activate leave if mouse cursor is outside... bounding box..
24578 if (this.currentTip) {
24579 this.currentTip.leave();
24581 //Roo.log('clear currentEl');
24582 this.currentEl = false;
24587 'left' : ['r-l', [-2,0], 'right'],
24588 'right' : ['l-r', [2,0], 'left'],
24589 'bottom' : ['t-b', [0,2], 'top'],
24590 'top' : [ 'b-t', [0,-2], 'bottom']
24596 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24601 delay : null, // can be { show : 300 , hide: 500}
24605 hoverState : null, //???
24607 placement : 'bottom',
24609 getAutoCreate : function(){
24616 cls : 'tooltip-arrow'
24619 cls : 'tooltip-inner'
24626 bind : function(el)
24632 enter : function () {
24634 if (this.timeout != null) {
24635 clearTimeout(this.timeout);
24638 this.hoverState = 'in';
24639 //Roo.log("enter - show");
24640 if (!this.delay || !this.delay.show) {
24645 this.timeout = setTimeout(function () {
24646 if (_t.hoverState == 'in') {
24649 }, this.delay.show);
24653 clearTimeout(this.timeout);
24655 this.hoverState = 'out';
24656 if (!this.delay || !this.delay.hide) {
24662 this.timeout = setTimeout(function () {
24663 //Roo.log("leave - timeout");
24665 if (_t.hoverState == 'out') {
24667 Roo.bootstrap.Tooltip.currentEl = false;
24675 this.render(document.body);
24678 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24680 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24682 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24684 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24686 var placement = typeof this.placement == 'function' ?
24687 this.placement.call(this, this.el, on_el) :
24690 var autoToken = /\s?auto?\s?/i;
24691 var autoPlace = autoToken.test(placement);
24693 placement = placement.replace(autoToken, '') || 'top';
24697 //this.el.setXY([0,0]);
24699 //this.el.dom.style.display='block';
24701 //this.el.appendTo(on_el);
24703 var p = this.getPosition();
24704 var box = this.el.getBox();
24710 var align = Roo.bootstrap.Tooltip.alignment[placement];
24712 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24714 if(placement == 'top' || placement == 'bottom'){
24716 placement = 'right';
24719 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24720 placement = 'left';
24723 var scroll = Roo.select('body', true).first().getScroll();
24725 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24731 align = Roo.bootstrap.Tooltip.alignment[placement];
24733 this.el.alignTo(this.bindEl, align[0],align[1]);
24734 //var arrow = this.el.select('.arrow',true).first();
24735 //arrow.set(align[2],
24737 this.el.addClass(placement);
24739 this.el.addClass('in fade');
24741 this.hoverState = null;
24743 if (this.el.hasClass('fade')) {
24754 //this.el.setXY([0,0]);
24755 this.el.removeClass('in');
24771 * @class Roo.bootstrap.LocationPicker
24772 * @extends Roo.bootstrap.Component
24773 * Bootstrap LocationPicker class
24774 * @cfg {Number} latitude Position when init default 0
24775 * @cfg {Number} longitude Position when init default 0
24776 * @cfg {Number} zoom default 15
24777 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24778 * @cfg {Boolean} mapTypeControl default false
24779 * @cfg {Boolean} disableDoubleClickZoom default false
24780 * @cfg {Boolean} scrollwheel default true
24781 * @cfg {Boolean} streetViewControl default false
24782 * @cfg {Number} radius default 0
24783 * @cfg {String} locationName
24784 * @cfg {Boolean} draggable default true
24785 * @cfg {Boolean} enableAutocomplete default false
24786 * @cfg {Boolean} enableReverseGeocode default true
24787 * @cfg {String} markerTitle
24790 * Create a new LocationPicker
24791 * @param {Object} config The config object
24795 Roo.bootstrap.LocationPicker = function(config){
24797 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24802 * Fires when the picker initialized.
24803 * @param {Roo.bootstrap.LocationPicker} this
24804 * @param {Google Location} location
24808 * @event positionchanged
24809 * Fires when the picker position changed.
24810 * @param {Roo.bootstrap.LocationPicker} this
24811 * @param {Google Location} location
24813 positionchanged : true,
24816 * Fires when the map resize.
24817 * @param {Roo.bootstrap.LocationPicker} this
24822 * Fires when the map show.
24823 * @param {Roo.bootstrap.LocationPicker} this
24828 * Fires when the map hide.
24829 * @param {Roo.bootstrap.LocationPicker} this
24834 * Fires when click the map.
24835 * @param {Roo.bootstrap.LocationPicker} this
24836 * @param {Map event} e
24840 * @event mapRightClick
24841 * Fires when right click the map.
24842 * @param {Roo.bootstrap.LocationPicker} this
24843 * @param {Map event} e
24845 mapRightClick : true,
24847 * @event markerClick
24848 * Fires when click the marker.
24849 * @param {Roo.bootstrap.LocationPicker} this
24850 * @param {Map event} e
24852 markerClick : true,
24854 * @event markerRightClick
24855 * Fires when right click the marker.
24856 * @param {Roo.bootstrap.LocationPicker} this
24857 * @param {Map event} e
24859 markerRightClick : true,
24861 * @event OverlayViewDraw
24862 * Fires when OverlayView Draw
24863 * @param {Roo.bootstrap.LocationPicker} this
24865 OverlayViewDraw : true,
24867 * @event OverlayViewOnAdd
24868 * Fires when OverlayView Draw
24869 * @param {Roo.bootstrap.LocationPicker} this
24871 OverlayViewOnAdd : true,
24873 * @event OverlayViewOnRemove
24874 * Fires when OverlayView Draw
24875 * @param {Roo.bootstrap.LocationPicker} this
24877 OverlayViewOnRemove : true,
24879 * @event OverlayViewShow
24880 * Fires when OverlayView Draw
24881 * @param {Roo.bootstrap.LocationPicker} this
24882 * @param {Pixel} cpx
24884 OverlayViewShow : true,
24886 * @event OverlayViewHide
24887 * Fires when OverlayView Draw
24888 * @param {Roo.bootstrap.LocationPicker} this
24890 OverlayViewHide : true,
24892 * @event loadexception
24893 * Fires when load google lib failed.
24894 * @param {Roo.bootstrap.LocationPicker} this
24896 loadexception : true
24901 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24903 gMapContext: false,
24909 mapTypeControl: false,
24910 disableDoubleClickZoom: false,
24912 streetViewControl: false,
24916 enableAutocomplete: false,
24917 enableReverseGeocode: true,
24920 getAutoCreate: function()
24925 cls: 'roo-location-picker'
24931 initEvents: function(ct, position)
24933 if(!this.el.getWidth() || this.isApplied()){
24937 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24942 initial: function()
24944 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24945 this.fireEvent('loadexception', this);
24949 if(!this.mapTypeId){
24950 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24953 this.gMapContext = this.GMapContext();
24955 this.initOverlayView();
24957 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24961 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24962 _this.setPosition(_this.gMapContext.marker.position);
24965 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24966 _this.fireEvent('mapClick', this, event);
24970 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24971 _this.fireEvent('mapRightClick', this, event);
24975 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24976 _this.fireEvent('markerClick', this, event);
24980 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24981 _this.fireEvent('markerRightClick', this, event);
24985 this.setPosition(this.gMapContext.location);
24987 this.fireEvent('initial', this, this.gMapContext.location);
24990 initOverlayView: function()
24994 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24998 _this.fireEvent('OverlayViewDraw', _this);
25003 _this.fireEvent('OverlayViewOnAdd', _this);
25006 onRemove: function()
25008 _this.fireEvent('OverlayViewOnRemove', _this);
25011 show: function(cpx)
25013 _this.fireEvent('OverlayViewShow', _this, cpx);
25018 _this.fireEvent('OverlayViewHide', _this);
25024 fromLatLngToContainerPixel: function(event)
25026 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25029 isApplied: function()
25031 return this.getGmapContext() == false ? false : true;
25034 getGmapContext: function()
25036 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25039 GMapContext: function()
25041 var position = new google.maps.LatLng(this.latitude, this.longitude);
25043 var _map = new google.maps.Map(this.el.dom, {
25046 mapTypeId: this.mapTypeId,
25047 mapTypeControl: this.mapTypeControl,
25048 disableDoubleClickZoom: this.disableDoubleClickZoom,
25049 scrollwheel: this.scrollwheel,
25050 streetViewControl: this.streetViewControl,
25051 locationName: this.locationName,
25052 draggable: this.draggable,
25053 enableAutocomplete: this.enableAutocomplete,
25054 enableReverseGeocode: this.enableReverseGeocode
25057 var _marker = new google.maps.Marker({
25058 position: position,
25060 title: this.markerTitle,
25061 draggable: this.draggable
25068 location: position,
25069 radius: this.radius,
25070 locationName: this.locationName,
25071 addressComponents: {
25072 formatted_address: null,
25073 addressLine1: null,
25074 addressLine2: null,
25076 streetNumber: null,
25080 stateOrProvince: null
25083 domContainer: this.el.dom,
25084 geodecoder: new google.maps.Geocoder()
25088 drawCircle: function(center, radius, options)
25090 if (this.gMapContext.circle != null) {
25091 this.gMapContext.circle.setMap(null);
25095 options = Roo.apply({}, options, {
25096 strokeColor: "#0000FF",
25097 strokeOpacity: .35,
25099 fillColor: "#0000FF",
25103 options.map = this.gMapContext.map;
25104 options.radius = radius;
25105 options.center = center;
25106 this.gMapContext.circle = new google.maps.Circle(options);
25107 return this.gMapContext.circle;
25113 setPosition: function(location)
25115 this.gMapContext.location = location;
25116 this.gMapContext.marker.setPosition(location);
25117 this.gMapContext.map.panTo(location);
25118 this.drawCircle(location, this.gMapContext.radius, {});
25122 if (this.gMapContext.settings.enableReverseGeocode) {
25123 this.gMapContext.geodecoder.geocode({
25124 latLng: this.gMapContext.location
25125 }, function(results, status) {
25127 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25128 _this.gMapContext.locationName = results[0].formatted_address;
25129 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25131 _this.fireEvent('positionchanged', this, location);
25138 this.fireEvent('positionchanged', this, location);
25143 google.maps.event.trigger(this.gMapContext.map, "resize");
25145 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25147 this.fireEvent('resize', this);
25150 setPositionByLatLng: function(latitude, longitude)
25152 this.setPosition(new google.maps.LatLng(latitude, longitude));
25155 getCurrentPosition: function()
25158 latitude: this.gMapContext.location.lat(),
25159 longitude: this.gMapContext.location.lng()
25163 getAddressName: function()
25165 return this.gMapContext.locationName;
25168 getAddressComponents: function()
25170 return this.gMapContext.addressComponents;
25173 address_component_from_google_geocode: function(address_components)
25177 for (var i = 0; i < address_components.length; i++) {
25178 var component = address_components[i];
25179 if (component.types.indexOf("postal_code") >= 0) {
25180 result.postalCode = component.short_name;
25181 } else if (component.types.indexOf("street_number") >= 0) {
25182 result.streetNumber = component.short_name;
25183 } else if (component.types.indexOf("route") >= 0) {
25184 result.streetName = component.short_name;
25185 } else if (component.types.indexOf("neighborhood") >= 0) {
25186 result.city = component.short_name;
25187 } else if (component.types.indexOf("locality") >= 0) {
25188 result.city = component.short_name;
25189 } else if (component.types.indexOf("sublocality") >= 0) {
25190 result.district = component.short_name;
25191 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25192 result.stateOrProvince = component.short_name;
25193 } else if (component.types.indexOf("country") >= 0) {
25194 result.country = component.short_name;
25198 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25199 result.addressLine2 = "";
25203 setZoomLevel: function(zoom)
25205 this.gMapContext.map.setZoom(zoom);
25218 this.fireEvent('show', this);
25229 this.fireEvent('hide', this);
25234 Roo.apply(Roo.bootstrap.LocationPicker, {
25236 OverlayView : function(map, options)
25238 options = options || {};
25252 * @class Roo.bootstrap.Alert
25253 * @extends Roo.bootstrap.Component
25254 * Bootstrap Alert class
25255 * @cfg {String} title The title of alert
25256 * @cfg {String} html The content of alert
25257 * @cfg {String} weight ( success | info | warning | danger )
25258 * @cfg {String} faicon font-awesomeicon
25261 * Create a new alert
25262 * @param {Object} config The config object
25266 Roo.bootstrap.Alert = function(config){
25267 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25271 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
25278 getAutoCreate : function()
25287 cls : 'roo-alert-icon'
25292 cls : 'roo-alert-title',
25297 cls : 'roo-alert-text',
25304 cfg.cn[0].cls += ' fa ' + this.faicon;
25308 cfg.cls += ' alert-' + this.weight;
25314 initEvents: function()
25316 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25319 setTitle : function(str)
25321 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25324 setText : function(str)
25326 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25329 setWeight : function(weight)
25332 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25335 this.weight = weight;
25337 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25340 setIcon : function(icon)
25343 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25346 this.faicon = icon;
25348 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25369 * @class Roo.bootstrap.UploadCropbox
25370 * @extends Roo.bootstrap.Component
25371 * Bootstrap UploadCropbox class
25372 * @cfg {String} emptyText show when image has been loaded
25373 * @cfg {String} rotateNotify show when image too small to rotate
25374 * @cfg {Number} errorTimeout default 3000
25375 * @cfg {Number} minWidth default 300
25376 * @cfg {Number} minHeight default 300
25377 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25378 * @cfg {Boolean} isDocument (true|false) default false
25379 * @cfg {String} url action url
25380 * @cfg {String} paramName default 'imageUpload'
25381 * @cfg {String} method default POST
25382 * @cfg {Boolean} loadMask (true|false) default true
25383 * @cfg {Boolean} loadingText default 'Loading...'
25386 * Create a new UploadCropbox
25387 * @param {Object} config The config object
25390 Roo.bootstrap.UploadCropbox = function(config){
25391 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25395 * @event beforeselectfile
25396 * Fire before select file
25397 * @param {Roo.bootstrap.UploadCropbox} this
25399 "beforeselectfile" : true,
25402 * Fire after initEvent
25403 * @param {Roo.bootstrap.UploadCropbox} this
25408 * Fire after initEvent
25409 * @param {Roo.bootstrap.UploadCropbox} this
25410 * @param {String} data
25415 * Fire when preparing the file data
25416 * @param {Roo.bootstrap.UploadCropbox} this
25417 * @param {Object} file
25422 * Fire when get exception
25423 * @param {Roo.bootstrap.UploadCropbox} this
25424 * @param {XMLHttpRequest} xhr
25426 "exception" : true,
25428 * @event beforeloadcanvas
25429 * Fire before load the canvas
25430 * @param {Roo.bootstrap.UploadCropbox} this
25431 * @param {String} src
25433 "beforeloadcanvas" : true,
25436 * Fire when trash image
25437 * @param {Roo.bootstrap.UploadCropbox} this
25442 * Fire when download the image
25443 * @param {Roo.bootstrap.UploadCropbox} this
25447 * @event footerbuttonclick
25448 * Fire when footerbuttonclick
25449 * @param {Roo.bootstrap.UploadCropbox} this
25450 * @param {String} type
25452 "footerbuttonclick" : true,
25456 * @param {Roo.bootstrap.UploadCropbox} this
25461 * Fire when rotate the image
25462 * @param {Roo.bootstrap.UploadCropbox} this
25463 * @param {String} pos
25468 * Fire when inspect the file
25469 * @param {Roo.bootstrap.UploadCropbox} this
25470 * @param {Object} file
25475 * Fire when xhr upload the file
25476 * @param {Roo.bootstrap.UploadCropbox} this
25477 * @param {Object} data
25482 * Fire when arrange the file data
25483 * @param {Roo.bootstrap.UploadCropbox} this
25484 * @param {Object} formData
25489 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25492 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25494 emptyText : 'Click to upload image',
25495 rotateNotify : 'Image is too small to rotate',
25496 errorTimeout : 3000,
25510 cropType : 'image/jpeg',
25512 canvasLoaded : false,
25513 isDocument : false,
25515 paramName : 'imageUpload',
25517 loadingText : 'Loading...',
25520 getAutoCreate : function()
25524 cls : 'roo-upload-cropbox',
25528 cls : 'roo-upload-cropbox-selector',
25533 cls : 'roo-upload-cropbox-body',
25534 style : 'cursor:pointer',
25538 cls : 'roo-upload-cropbox-preview'
25542 cls : 'roo-upload-cropbox-thumb'
25546 cls : 'roo-upload-cropbox-empty-notify',
25547 html : this.emptyText
25551 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25552 html : this.rotateNotify
25558 cls : 'roo-upload-cropbox-footer',
25561 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25571 onRender : function(ct, position)
25573 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25575 if (this.buttons.length) {
25577 Roo.each(this.buttons, function(bb) {
25579 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25581 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25587 this.maskEl = this.el;
25591 initEvents : function()
25593 this.urlAPI = (window.createObjectURL && window) ||
25594 (window.URL && URL.revokeObjectURL && URL) ||
25595 (window.webkitURL && webkitURL);
25597 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25598 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25600 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25601 this.selectorEl.hide();
25603 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25604 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25606 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25607 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25608 this.thumbEl.hide();
25610 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25611 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25613 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25614 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25615 this.errorEl.hide();
25617 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25618 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25619 this.footerEl.hide();
25621 this.setThumbBoxSize();
25627 this.fireEvent('initial', this);
25634 window.addEventListener("resize", function() { _this.resize(); } );
25636 this.bodyEl.on('click', this.beforeSelectFile, this);
25639 this.bodyEl.on('touchstart', this.onTouchStart, this);
25640 this.bodyEl.on('touchmove', this.onTouchMove, this);
25641 this.bodyEl.on('touchend', this.onTouchEnd, this);
25645 this.bodyEl.on('mousedown', this.onMouseDown, this);
25646 this.bodyEl.on('mousemove', this.onMouseMove, this);
25647 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25648 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25649 Roo.get(document).on('mouseup', this.onMouseUp, this);
25652 this.selectorEl.on('change', this.onFileSelected, this);
25658 this.baseScale = 1;
25660 this.baseRotate = 1;
25661 this.dragable = false;
25662 this.pinching = false;
25665 this.cropData = false;
25666 this.notifyEl.dom.innerHTML = this.emptyText;
25668 this.selectorEl.dom.value = '';
25672 resize : function()
25674 if(this.fireEvent('resize', this) != false){
25675 this.setThumbBoxPosition();
25676 this.setCanvasPosition();
25680 onFooterButtonClick : function(e, el, o, type)
25683 case 'rotate-left' :
25684 this.onRotateLeft(e);
25686 case 'rotate-right' :
25687 this.onRotateRight(e);
25690 this.beforeSelectFile(e);
25705 this.fireEvent('footerbuttonclick', this, type);
25708 beforeSelectFile : function(e)
25710 e.preventDefault();
25712 if(this.fireEvent('beforeselectfile', this) != false){
25713 this.selectorEl.dom.click();
25717 onFileSelected : function(e)
25719 e.preventDefault();
25721 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25725 var file = this.selectorEl.dom.files[0];
25727 if(this.fireEvent('inspect', this, file) != false){
25728 this.prepare(file);
25733 trash : function(e)
25735 this.fireEvent('trash', this);
25738 download : function(e)
25740 this.fireEvent('download', this);
25743 loadCanvas : function(src)
25745 if(this.fireEvent('beforeloadcanvas', this, src) != false){
25749 this.imageEl = document.createElement('img');
25753 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25755 this.imageEl.src = src;
25759 onLoadCanvas : function()
25761 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25762 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25764 this.bodyEl.un('click', this.beforeSelectFile, this);
25766 this.notifyEl.hide();
25767 this.thumbEl.show();
25768 this.footerEl.show();
25770 this.baseRotateLevel();
25772 if(this.isDocument){
25773 this.setThumbBoxSize();
25776 this.setThumbBoxPosition();
25778 this.baseScaleLevel();
25784 this.canvasLoaded = true;
25787 this.maskEl.unmask();
25792 setCanvasPosition : function()
25794 if(!this.canvasEl){
25798 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25799 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25801 this.previewEl.setLeft(pw);
25802 this.previewEl.setTop(ph);
25806 onMouseDown : function(e)
25810 this.dragable = true;
25811 this.pinching = false;
25813 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25814 this.dragable = false;
25818 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25819 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25823 onMouseMove : function(e)
25827 if(!this.canvasLoaded){
25831 if (!this.dragable){
25835 var minX = Math.ceil(this.thumbEl.getLeft(true));
25836 var minY = Math.ceil(this.thumbEl.getTop(true));
25838 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25839 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25841 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25842 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25844 x = x - this.mouseX;
25845 y = y - this.mouseY;
25847 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25848 var bgY = Math.ceil(y + this.previewEl.getTop(true));
25850 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25851 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25853 this.previewEl.setLeft(bgX);
25854 this.previewEl.setTop(bgY);
25856 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25857 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25860 onMouseUp : function(e)
25864 this.dragable = false;
25867 onMouseWheel : function(e)
25871 this.startScale = this.scale;
25873 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25875 if(!this.zoomable()){
25876 this.scale = this.startScale;
25885 zoomable : function()
25887 var minScale = this.thumbEl.getWidth() / this.minWidth;
25889 if(this.minWidth < this.minHeight){
25890 minScale = this.thumbEl.getHeight() / this.minHeight;
25893 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25894 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25898 (this.rotate == 0 || this.rotate == 180) &&
25900 width > this.imageEl.OriginWidth ||
25901 height > this.imageEl.OriginHeight ||
25902 (width < this.minWidth && height < this.minHeight)
25910 (this.rotate == 90 || this.rotate == 270) &&
25912 width > this.imageEl.OriginWidth ||
25913 height > this.imageEl.OriginHeight ||
25914 (width < this.minHeight && height < this.minWidth)
25921 !this.isDocument &&
25922 (this.rotate == 0 || this.rotate == 180) &&
25924 width < this.minWidth ||
25925 width > this.imageEl.OriginWidth ||
25926 height < this.minHeight ||
25927 height > this.imageEl.OriginHeight
25934 !this.isDocument &&
25935 (this.rotate == 90 || this.rotate == 270) &&
25937 width < this.minHeight ||
25938 width > this.imageEl.OriginWidth ||
25939 height < this.minWidth ||
25940 height > this.imageEl.OriginHeight
25950 onRotateLeft : function(e)
25952 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25954 var minScale = this.thumbEl.getWidth() / this.minWidth;
25956 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25957 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25959 this.startScale = this.scale;
25961 while (this.getScaleLevel() < minScale){
25963 this.scale = this.scale + 1;
25965 if(!this.zoomable()){
25970 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25971 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25976 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25983 this.scale = this.startScale;
25985 this.onRotateFail();
25990 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25992 if(this.isDocument){
25993 this.setThumbBoxSize();
25994 this.setThumbBoxPosition();
25995 this.setCanvasPosition();
26000 this.fireEvent('rotate', this, 'left');
26004 onRotateRight : function(e)
26006 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26008 var minScale = this.thumbEl.getWidth() / this.minWidth;
26010 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26011 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26013 this.startScale = this.scale;
26015 while (this.getScaleLevel() < minScale){
26017 this.scale = this.scale + 1;
26019 if(!this.zoomable()){
26024 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26025 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26030 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26037 this.scale = this.startScale;
26039 this.onRotateFail();
26044 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26046 if(this.isDocument){
26047 this.setThumbBoxSize();
26048 this.setThumbBoxPosition();
26049 this.setCanvasPosition();
26054 this.fireEvent('rotate', this, 'right');
26057 onRotateFail : function()
26059 this.errorEl.show(true);
26063 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26068 this.previewEl.dom.innerHTML = '';
26070 var canvasEl = document.createElement("canvas");
26072 var contextEl = canvasEl.getContext("2d");
26074 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26075 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26076 var center = this.imageEl.OriginWidth / 2;
26078 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26079 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26080 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26081 center = this.imageEl.OriginHeight / 2;
26084 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26086 contextEl.translate(center, center);
26087 contextEl.rotate(this.rotate * Math.PI / 180);
26089 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26091 this.canvasEl = document.createElement("canvas");
26093 this.contextEl = this.canvasEl.getContext("2d");
26095 switch (this.rotate) {
26098 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26099 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26101 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26106 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26107 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26109 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26110 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);
26114 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26119 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26120 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26122 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26123 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);
26127 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);
26132 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26133 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26135 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26136 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26140 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);
26147 this.previewEl.appendChild(this.canvasEl);
26149 this.setCanvasPosition();
26154 if(!this.canvasLoaded){
26158 var imageCanvas = document.createElement("canvas");
26160 var imageContext = imageCanvas.getContext("2d");
26162 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26163 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26165 var center = imageCanvas.width / 2;
26167 imageContext.translate(center, center);
26169 imageContext.rotate(this.rotate * Math.PI / 180);
26171 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26173 var canvas = document.createElement("canvas");
26175 var context = canvas.getContext("2d");
26177 canvas.width = this.minWidth;
26178 canvas.height = this.minHeight;
26180 switch (this.rotate) {
26183 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26184 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26186 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26187 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26189 var targetWidth = this.minWidth - 2 * x;
26190 var targetHeight = this.minHeight - 2 * y;
26194 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26195 scale = targetWidth / width;
26198 if(x > 0 && y == 0){
26199 scale = targetHeight / height;
26202 if(x > 0 && y > 0){
26203 scale = targetWidth / width;
26205 if(width < height){
26206 scale = targetHeight / height;
26210 context.scale(scale, scale);
26212 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26213 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26215 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26216 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26218 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26223 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26224 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26226 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26227 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26229 var targetWidth = this.minWidth - 2 * x;
26230 var targetHeight = this.minHeight - 2 * y;
26234 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26235 scale = targetWidth / width;
26238 if(x > 0 && y == 0){
26239 scale = targetHeight / height;
26242 if(x > 0 && y > 0){
26243 scale = targetWidth / width;
26245 if(width < height){
26246 scale = targetHeight / height;
26250 context.scale(scale, scale);
26252 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26253 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26255 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26256 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26258 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26260 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26265 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26266 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26268 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26269 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26271 var targetWidth = this.minWidth - 2 * x;
26272 var targetHeight = this.minHeight - 2 * y;
26276 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26277 scale = targetWidth / width;
26280 if(x > 0 && y == 0){
26281 scale = targetHeight / height;
26284 if(x > 0 && y > 0){
26285 scale = targetWidth / width;
26287 if(width < height){
26288 scale = targetHeight / height;
26292 context.scale(scale, scale);
26294 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26295 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26297 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26298 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26300 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26301 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26303 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26308 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26309 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26311 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26312 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26314 var targetWidth = this.minWidth - 2 * x;
26315 var targetHeight = this.minHeight - 2 * y;
26319 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26320 scale = targetWidth / width;
26323 if(x > 0 && y == 0){
26324 scale = targetHeight / height;
26327 if(x > 0 && y > 0){
26328 scale = targetWidth / width;
26330 if(width < height){
26331 scale = targetHeight / height;
26335 context.scale(scale, scale);
26337 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26338 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26340 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26341 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26343 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26345 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26352 this.cropData = canvas.toDataURL(this.cropType);
26354 if(this.fireEvent('crop', this, this.cropData) !== false){
26355 this.process(this.file, this.cropData);
26362 setThumbBoxSize : function()
26366 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26367 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26368 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26370 this.minWidth = width;
26371 this.minHeight = height;
26373 if(this.rotate == 90 || this.rotate == 270){
26374 this.minWidth = height;
26375 this.minHeight = width;
26380 width = Math.ceil(this.minWidth * height / this.minHeight);
26382 if(this.minWidth > this.minHeight){
26384 height = Math.ceil(this.minHeight * width / this.minWidth);
26387 this.thumbEl.setStyle({
26388 width : width + 'px',
26389 height : height + 'px'
26396 setThumbBoxPosition : function()
26398 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26399 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26401 this.thumbEl.setLeft(x);
26402 this.thumbEl.setTop(y);
26406 baseRotateLevel : function()
26408 this.baseRotate = 1;
26411 typeof(this.exif) != 'undefined' &&
26412 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26413 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26415 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26418 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26422 baseScaleLevel : function()
26426 if(this.isDocument){
26428 if(this.baseRotate == 6 || this.baseRotate == 8){
26430 height = this.thumbEl.getHeight();
26431 this.baseScale = height / this.imageEl.OriginWidth;
26433 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26434 width = this.thumbEl.getWidth();
26435 this.baseScale = width / this.imageEl.OriginHeight;
26441 height = this.thumbEl.getHeight();
26442 this.baseScale = height / this.imageEl.OriginHeight;
26444 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26445 width = this.thumbEl.getWidth();
26446 this.baseScale = width / this.imageEl.OriginWidth;
26452 if(this.baseRotate == 6 || this.baseRotate == 8){
26454 width = this.thumbEl.getHeight();
26455 this.baseScale = width / this.imageEl.OriginHeight;
26457 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26458 height = this.thumbEl.getWidth();
26459 this.baseScale = height / this.imageEl.OriginHeight;
26462 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26463 height = this.thumbEl.getWidth();
26464 this.baseScale = height / this.imageEl.OriginHeight;
26466 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26467 width = this.thumbEl.getHeight();
26468 this.baseScale = width / this.imageEl.OriginWidth;
26475 width = this.thumbEl.getWidth();
26476 this.baseScale = width / this.imageEl.OriginWidth;
26478 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26479 height = this.thumbEl.getHeight();
26480 this.baseScale = height / this.imageEl.OriginHeight;
26483 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26485 height = this.thumbEl.getHeight();
26486 this.baseScale = height / this.imageEl.OriginHeight;
26488 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26489 width = this.thumbEl.getWidth();
26490 this.baseScale = width / this.imageEl.OriginWidth;
26498 getScaleLevel : function()
26500 return this.baseScale * Math.pow(1.1, this.scale);
26503 onTouchStart : function(e)
26505 if(!this.canvasLoaded){
26506 this.beforeSelectFile(e);
26510 var touches = e.browserEvent.touches;
26516 if(touches.length == 1){
26517 this.onMouseDown(e);
26521 if(touches.length != 2){
26527 for(var i = 0, finger; finger = touches[i]; i++){
26528 coords.push(finger.pageX, finger.pageY);
26531 var x = Math.pow(coords[0] - coords[2], 2);
26532 var y = Math.pow(coords[1] - coords[3], 2);
26534 this.startDistance = Math.sqrt(x + y);
26536 this.startScale = this.scale;
26538 this.pinching = true;
26539 this.dragable = false;
26543 onTouchMove : function(e)
26545 if(!this.pinching && !this.dragable){
26549 var touches = e.browserEvent.touches;
26556 this.onMouseMove(e);
26562 for(var i = 0, finger; finger = touches[i]; i++){
26563 coords.push(finger.pageX, finger.pageY);
26566 var x = Math.pow(coords[0] - coords[2], 2);
26567 var y = Math.pow(coords[1] - coords[3], 2);
26569 this.endDistance = Math.sqrt(x + y);
26571 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26573 if(!this.zoomable()){
26574 this.scale = this.startScale;
26582 onTouchEnd : function(e)
26584 this.pinching = false;
26585 this.dragable = false;
26589 process : function(file, crop)
26592 this.maskEl.mask(this.loadingText);
26595 this.xhr = new XMLHttpRequest();
26597 file.xhr = this.xhr;
26599 this.xhr.open(this.method, this.url, true);
26602 "Accept": "application/json",
26603 "Cache-Control": "no-cache",
26604 "X-Requested-With": "XMLHttpRequest"
26607 for (var headerName in headers) {
26608 var headerValue = headers[headerName];
26610 this.xhr.setRequestHeader(headerName, headerValue);
26616 this.xhr.onload = function()
26618 _this.xhrOnLoad(_this.xhr);
26621 this.xhr.onerror = function()
26623 _this.xhrOnError(_this.xhr);
26626 var formData = new FormData();
26628 formData.append('returnHTML', 'NO');
26631 formData.append('crop', crop);
26634 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26635 formData.append(this.paramName, file, file.name);
26638 if(typeof(file.filename) != 'undefined'){
26639 formData.append('filename', file.filename);
26642 if(typeof(file.mimetype) != 'undefined'){
26643 formData.append('mimetype', file.mimetype);
26646 if(this.fireEvent('arrange', this, formData) != false){
26647 this.xhr.send(formData);
26651 xhrOnLoad : function(xhr)
26654 this.maskEl.unmask();
26657 if (xhr.readyState !== 4) {
26658 this.fireEvent('exception', this, xhr);
26662 var response = Roo.decode(xhr.responseText);
26664 if(!response.success){
26665 this.fireEvent('exception', this, xhr);
26669 var response = Roo.decode(xhr.responseText);
26671 this.fireEvent('upload', this, response);
26675 xhrOnError : function()
26678 this.maskEl.unmask();
26681 Roo.log('xhr on error');
26683 var response = Roo.decode(xhr.responseText);
26689 prepare : function(file)
26692 this.maskEl.mask(this.loadingText);
26698 if(typeof(file) === 'string'){
26699 this.loadCanvas(file);
26703 if(!file || !this.urlAPI){
26708 this.cropType = file.type;
26712 if(this.fireEvent('prepare', this, this.file) != false){
26714 var reader = new FileReader();
26716 reader.onload = function (e) {
26717 if (e.target.error) {
26718 Roo.log(e.target.error);
26722 var buffer = e.target.result,
26723 dataView = new DataView(buffer),
26725 maxOffset = dataView.byteLength - 4,
26729 if (dataView.getUint16(0) === 0xffd8) {
26730 while (offset < maxOffset) {
26731 markerBytes = dataView.getUint16(offset);
26733 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26734 markerLength = dataView.getUint16(offset + 2) + 2;
26735 if (offset + markerLength > dataView.byteLength) {
26736 Roo.log('Invalid meta data: Invalid segment size.');
26740 if(markerBytes == 0xffe1){
26741 _this.parseExifData(
26748 offset += markerLength;
26758 var url = _this.urlAPI.createObjectURL(_this.file);
26760 _this.loadCanvas(url);
26765 reader.readAsArrayBuffer(this.file);
26771 parseExifData : function(dataView, offset, length)
26773 var tiffOffset = offset + 10,
26777 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26778 // No Exif data, might be XMP data instead
26782 // Check for the ASCII code for "Exif" (0x45786966):
26783 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26784 // No Exif data, might be XMP data instead
26787 if (tiffOffset + 8 > dataView.byteLength) {
26788 Roo.log('Invalid Exif data: Invalid segment size.');
26791 // Check for the two null bytes:
26792 if (dataView.getUint16(offset + 8) !== 0x0000) {
26793 Roo.log('Invalid Exif data: Missing byte alignment offset.');
26796 // Check the byte alignment:
26797 switch (dataView.getUint16(tiffOffset)) {
26799 littleEndian = true;
26802 littleEndian = false;
26805 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26808 // Check for the TIFF tag marker (0x002A):
26809 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26810 Roo.log('Invalid Exif data: Missing TIFF marker.');
26813 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26814 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26816 this.parseExifTags(
26819 tiffOffset + dirOffset,
26824 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26829 if (dirOffset + 6 > dataView.byteLength) {
26830 Roo.log('Invalid Exif data: Invalid directory offset.');
26833 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26834 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26835 if (dirEndOffset + 4 > dataView.byteLength) {
26836 Roo.log('Invalid Exif data: Invalid directory size.');
26839 for (i = 0; i < tagsNumber; i += 1) {
26843 dirOffset + 2 + 12 * i, // tag offset
26847 // Return the offset to the next directory:
26848 return dataView.getUint32(dirEndOffset, littleEndian);
26851 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
26853 var tag = dataView.getUint16(offset, littleEndian);
26855 this.exif[tag] = this.getExifValue(
26859 dataView.getUint16(offset + 2, littleEndian), // tag type
26860 dataView.getUint32(offset + 4, littleEndian), // tag length
26865 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26867 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26876 Roo.log('Invalid Exif data: Invalid tag type.');
26880 tagSize = tagType.size * length;
26881 // Determine if the value is contained in the dataOffset bytes,
26882 // or if the value at the dataOffset is a pointer to the actual data:
26883 dataOffset = tagSize > 4 ?
26884 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26885 if (dataOffset + tagSize > dataView.byteLength) {
26886 Roo.log('Invalid Exif data: Invalid data offset.');
26889 if (length === 1) {
26890 return tagType.getValue(dataView, dataOffset, littleEndian);
26893 for (i = 0; i < length; i += 1) {
26894 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26897 if (tagType.ascii) {
26899 // Concatenate the chars:
26900 for (i = 0; i < values.length; i += 1) {
26902 // Ignore the terminating NULL byte(s):
26903 if (c === '\u0000') {
26915 Roo.apply(Roo.bootstrap.UploadCropbox, {
26917 'Orientation': 0x0112
26921 1: 0, //'top-left',
26923 3: 180, //'bottom-right',
26924 // 4: 'bottom-left',
26926 6: 90, //'right-top',
26927 // 7: 'right-bottom',
26928 8: 270 //'left-bottom'
26932 // byte, 8-bit unsigned int:
26934 getValue: function (dataView, dataOffset) {
26935 return dataView.getUint8(dataOffset);
26939 // ascii, 8-bit byte:
26941 getValue: function (dataView, dataOffset) {
26942 return String.fromCharCode(dataView.getUint8(dataOffset));
26947 // short, 16 bit int:
26949 getValue: function (dataView, dataOffset, littleEndian) {
26950 return dataView.getUint16(dataOffset, littleEndian);
26954 // long, 32 bit int:
26956 getValue: function (dataView, dataOffset, littleEndian) {
26957 return dataView.getUint32(dataOffset, littleEndian);
26961 // rational = two long values, first is numerator, second is denominator:
26963 getValue: function (dataView, dataOffset, littleEndian) {
26964 return dataView.getUint32(dataOffset, littleEndian) /
26965 dataView.getUint32(dataOffset + 4, littleEndian);
26969 // slong, 32 bit signed int:
26971 getValue: function (dataView, dataOffset, littleEndian) {
26972 return dataView.getInt32(dataOffset, littleEndian);
26976 // srational, two slongs, first is numerator, second is denominator:
26978 getValue: function (dataView, dataOffset, littleEndian) {
26979 return dataView.getInt32(dataOffset, littleEndian) /
26980 dataView.getInt32(dataOffset + 4, littleEndian);
26990 cls : 'btn-group roo-upload-cropbox-rotate-left',
26991 action : 'rotate-left',
26995 cls : 'btn btn-default',
26996 html : '<i class="fa fa-undo"></i>'
27002 cls : 'btn-group roo-upload-cropbox-picture',
27003 action : 'picture',
27007 cls : 'btn btn-default',
27008 html : '<i class="fa fa-picture-o"></i>'
27014 cls : 'btn-group roo-upload-cropbox-rotate-right',
27015 action : 'rotate-right',
27019 cls : 'btn btn-default',
27020 html : '<i class="fa fa-repeat"></i>'
27028 cls : 'btn-group roo-upload-cropbox-rotate-left',
27029 action : 'rotate-left',
27033 cls : 'btn btn-default',
27034 html : '<i class="fa fa-undo"></i>'
27040 cls : 'btn-group roo-upload-cropbox-download',
27041 action : 'download',
27045 cls : 'btn btn-default',
27046 html : '<i class="fa fa-download"></i>'
27052 cls : 'btn-group roo-upload-cropbox-crop',
27057 cls : 'btn btn-default',
27058 html : '<i class="fa fa-crop"></i>'
27064 cls : 'btn-group roo-upload-cropbox-trash',
27069 cls : 'btn btn-default',
27070 html : '<i class="fa fa-trash"></i>'
27076 cls : 'btn-group roo-upload-cropbox-rotate-right',
27077 action : 'rotate-right',
27081 cls : 'btn btn-default',
27082 html : '<i class="fa fa-repeat"></i>'
27090 cls : 'btn-group roo-upload-cropbox-rotate-left',
27091 action : 'rotate-left',
27095 cls : 'btn btn-default',
27096 html : '<i class="fa fa-undo"></i>'
27102 cls : 'btn-group roo-upload-cropbox-rotate-right',
27103 action : 'rotate-right',
27107 cls : 'btn btn-default',
27108 html : '<i class="fa fa-repeat"></i>'
27121 * @class Roo.bootstrap.DocumentManager
27122 * @extends Roo.bootstrap.Component
27123 * Bootstrap DocumentManager class
27124 * @cfg {String} paramName default 'imageUpload'
27125 * @cfg {String} method default POST
27126 * @cfg {String} url action url
27127 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27128 * @cfg {Boolean} multiple multiple upload default true
27129 * @cfg {Number} thumbSize default 300
27130 * @cfg {String} fieldLabel
27131 * @cfg {Number} labelWidth default 4
27132 * @cfg {String} labelAlign (left|top) default left
27133 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27136 * Create a new DocumentManager
27137 * @param {Object} config The config object
27140 Roo.bootstrap.DocumentManager = function(config){
27141 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27146 * Fire when initial the DocumentManager
27147 * @param {Roo.bootstrap.DocumentManager} this
27152 * inspect selected file
27153 * @param {Roo.bootstrap.DocumentManager} this
27154 * @param {File} file
27159 * Fire when xhr load exception
27160 * @param {Roo.bootstrap.DocumentManager} this
27161 * @param {XMLHttpRequest} xhr
27163 "exception" : true,
27166 * prepare the form data
27167 * @param {Roo.bootstrap.DocumentManager} this
27168 * @param {Object} formData
27173 * Fire when remove the file
27174 * @param {Roo.bootstrap.DocumentManager} this
27175 * @param {Object} file
27180 * Fire after refresh the file
27181 * @param {Roo.bootstrap.DocumentManager} this
27186 * Fire after click the image
27187 * @param {Roo.bootstrap.DocumentManager} this
27188 * @param {Object} file
27193 * Fire when upload a image and editable set to true
27194 * @param {Roo.bootstrap.DocumentManager} this
27195 * @param {Object} file
27199 * @event beforeselectfile
27200 * Fire before select file
27201 * @param {Roo.bootstrap.DocumentManager} this
27203 "beforeselectfile" : true,
27206 * Fire before process file
27207 * @param {Roo.bootstrap.DocumentManager} this
27208 * @param {Object} file
27215 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
27224 paramName : 'imageUpload',
27227 labelAlign : 'left',
27234 getAutoCreate : function()
27236 var managerWidget = {
27238 cls : 'roo-document-manager',
27242 cls : 'roo-document-manager-selector',
27247 cls : 'roo-document-manager-uploader',
27251 cls : 'roo-document-manager-upload-btn',
27252 html : '<i class="fa fa-plus"></i>'
27263 cls : 'column col-md-12',
27268 if(this.fieldLabel.length){
27273 cls : 'column col-md-12',
27274 html : this.fieldLabel
27278 cls : 'column col-md-12',
27283 if(this.labelAlign == 'left'){
27287 cls : 'column col-md-' + this.labelWidth,
27288 html : this.fieldLabel
27292 cls : 'column col-md-' + (12 - this.labelWidth),
27302 cls : 'row clearfix',
27310 initEvents : function()
27312 this.managerEl = this.el.select('.roo-document-manager', true).first();
27313 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27315 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27316 this.selectorEl.hide();
27319 this.selectorEl.attr('multiple', 'multiple');
27322 this.selectorEl.on('change', this.onFileSelected, this);
27324 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27325 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27327 this.uploader.on('click', this.onUploaderClick, this);
27329 this.renderProgressDialog();
27333 window.addEventListener("resize", function() { _this.refresh(); } );
27335 this.fireEvent('initial', this);
27338 renderProgressDialog : function()
27342 this.progressDialog = new Roo.bootstrap.Modal({
27343 cls : 'roo-document-manager-progress-dialog',
27344 allow_close : false,
27354 btnclick : function() {
27355 _this.uploadCancel();
27361 this.progressDialog.render(Roo.get(document.body));
27363 this.progress = new Roo.bootstrap.Progress({
27364 cls : 'roo-document-manager-progress',
27369 this.progress.render(this.progressDialog.getChildContainer());
27371 this.progressBar = new Roo.bootstrap.ProgressBar({
27372 cls : 'roo-document-manager-progress-bar',
27375 aria_valuemax : 12,
27379 this.progressBar.render(this.progress.getChildContainer());
27382 onUploaderClick : function(e)
27384 e.preventDefault();
27386 if(this.fireEvent('beforeselectfile', this) != false){
27387 this.selectorEl.dom.click();
27392 onFileSelected : function(e)
27394 e.preventDefault();
27396 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27400 Roo.each(this.selectorEl.dom.files, function(file){
27401 if(this.fireEvent('inspect', this, file) != false){
27402 this.files.push(file);
27412 this.selectorEl.dom.value = '';
27414 if(!this.files.length){
27418 if(this.boxes > 0 && this.files.length > this.boxes){
27419 this.files = this.files.slice(0, this.boxes);
27422 this.uploader.show();
27424 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27425 this.uploader.hide();
27434 Roo.each(this.files, function(file){
27436 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27437 var f = this.renderPreview(file);
27442 if(file.type.indexOf('image') != -1){
27443 this.delegates.push(
27445 _this.process(file);
27446 }).createDelegate(this)
27454 _this.process(file);
27455 }).createDelegate(this)
27460 this.files = files;
27462 this.delegates = this.delegates.concat(docs);
27464 if(!this.delegates.length){
27469 this.progressBar.aria_valuemax = this.delegates.length;
27476 arrange : function()
27478 if(!this.delegates.length){
27479 this.progressDialog.hide();
27484 var delegate = this.delegates.shift();
27486 this.progressDialog.show();
27488 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27490 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27495 refresh : function()
27497 this.uploader.show();
27499 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27500 this.uploader.hide();
27503 Roo.isTouch ? this.closable(false) : this.closable(true);
27505 this.fireEvent('refresh', this);
27508 onRemove : function(e, el, o)
27510 e.preventDefault();
27512 this.fireEvent('remove', this, o);
27516 remove : function(o)
27520 Roo.each(this.files, function(file){
27521 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27530 this.files = files;
27537 Roo.each(this.files, function(file){
27542 file.target.remove();
27551 onClick : function(e, el, o)
27553 e.preventDefault();
27555 this.fireEvent('click', this, o);
27559 closable : function(closable)
27561 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27563 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27575 xhrOnLoad : function(xhr)
27577 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27581 if (xhr.readyState !== 4) {
27583 this.fireEvent('exception', this, xhr);
27587 var response = Roo.decode(xhr.responseText);
27589 if(!response.success){
27591 this.fireEvent('exception', this, xhr);
27595 var file = this.renderPreview(response.data);
27597 this.files.push(file);
27603 xhrOnError : function(xhr)
27605 Roo.log('xhr on error');
27607 var response = Roo.decode(xhr.responseText);
27614 process : function(file)
27616 if(this.fireEvent('process', this, file) !== false){
27617 if(this.editable && file.type.indexOf('image') != -1){
27618 this.fireEvent('edit', this, file);
27622 this.uploadStart(file, false);
27629 uploadStart : function(file, crop)
27631 this.xhr = new XMLHttpRequest();
27633 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27638 file.xhr = this.xhr;
27640 this.managerEl.createChild({
27642 cls : 'roo-document-manager-loading',
27646 tooltip : file.name,
27647 cls : 'roo-document-manager-thumb',
27648 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27654 this.xhr.open(this.method, this.url, true);
27657 "Accept": "application/json",
27658 "Cache-Control": "no-cache",
27659 "X-Requested-With": "XMLHttpRequest"
27662 for (var headerName in headers) {
27663 var headerValue = headers[headerName];
27665 this.xhr.setRequestHeader(headerName, headerValue);
27671 this.xhr.onload = function()
27673 _this.xhrOnLoad(_this.xhr);
27676 this.xhr.onerror = function()
27678 _this.xhrOnError(_this.xhr);
27681 var formData = new FormData();
27683 formData.append('returnHTML', 'NO');
27686 formData.append('crop', crop);
27689 formData.append(this.paramName, file, file.name);
27691 if(this.fireEvent('prepare', this, formData) != false){
27692 this.xhr.send(formData);
27696 uploadCancel : function()
27703 this.delegates = [];
27705 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27712 renderPreview : function(file)
27714 if(typeof(file.target) != 'undefined' && file.target){
27718 var previewEl = this.managerEl.createChild({
27720 cls : 'roo-document-manager-preview',
27724 tooltip : file.filename,
27725 cls : 'roo-document-manager-thumb',
27726 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27731 html : '<i class="fa fa-times-circle"></i>'
27736 var close = previewEl.select('button.close', true).first();
27738 close.on('click', this.onRemove, this, file);
27740 file.target = previewEl;
27742 var image = previewEl.select('img', true).first();
27746 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27748 image.on('click', this.onClick, this, file);
27754 onPreviewLoad : function(file, image)
27756 if(typeof(file.target) == 'undefined' || !file.target){
27760 var width = image.dom.naturalWidth || image.dom.width;
27761 var height = image.dom.naturalHeight || image.dom.height;
27763 if(width > height){
27764 file.target.addClass('wide');
27768 file.target.addClass('tall');
27773 uploadFromSource : function(file, crop)
27775 this.xhr = new XMLHttpRequest();
27777 this.managerEl.createChild({
27779 cls : 'roo-document-manager-loading',
27783 tooltip : file.name,
27784 cls : 'roo-document-manager-thumb',
27785 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27791 this.xhr.open(this.method, this.url, true);
27794 "Accept": "application/json",
27795 "Cache-Control": "no-cache",
27796 "X-Requested-With": "XMLHttpRequest"
27799 for (var headerName in headers) {
27800 var headerValue = headers[headerName];
27802 this.xhr.setRequestHeader(headerName, headerValue);
27808 this.xhr.onload = function()
27810 _this.xhrOnLoad(_this.xhr);
27813 this.xhr.onerror = function()
27815 _this.xhrOnError(_this.xhr);
27818 var formData = new FormData();
27820 formData.append('returnHTML', 'NO');
27822 formData.append('crop', crop);
27824 if(typeof(file.filename) != 'undefined'){
27825 formData.append('filename', file.filename);
27828 if(typeof(file.mimetype) != 'undefined'){
27829 formData.append('mimetype', file.mimetype);
27832 if(this.fireEvent('prepare', this, formData) != false){
27833 this.xhr.send(formData);
27843 * @class Roo.bootstrap.DocumentViewer
27844 * @extends Roo.bootstrap.Component
27845 * Bootstrap DocumentViewer class
27848 * Create a new DocumentViewer
27849 * @param {Object} config The config object
27852 Roo.bootstrap.DocumentViewer = function(config){
27853 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27858 * Fire after initEvent
27859 * @param {Roo.bootstrap.DocumentViewer} this
27865 * @param {Roo.bootstrap.DocumentViewer} this
27870 * Fire after trash button
27871 * @param {Roo.bootstrap.DocumentViewer} this
27878 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27880 getAutoCreate : function()
27884 cls : 'roo-document-viewer',
27888 cls : 'roo-document-viewer-body',
27892 cls : 'roo-document-viewer-thumb',
27896 cls : 'roo-document-viewer-image'
27904 cls : 'roo-document-viewer-footer',
27907 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27915 cls : 'btn btn-default roo-document-viewer-trash',
27916 html : '<i class="fa fa-trash"></i>'
27929 initEvents : function()
27932 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27933 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27935 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27936 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27938 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27939 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27941 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27942 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27944 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27945 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27947 this.bodyEl.on('click', this.onClick, this);
27949 this.trashBtn.on('click', this.onTrash, this);
27953 initial : function()
27955 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27958 this.fireEvent('initial', this);
27962 onClick : function(e)
27964 e.preventDefault();
27966 this.fireEvent('click', this);
27969 onTrash : function(e)
27971 e.preventDefault();
27973 this.fireEvent('trash', this);
27985 * @class Roo.bootstrap.NavProgressBar
27986 * @extends Roo.bootstrap.Component
27987 * Bootstrap NavProgressBar class
27990 * Create a new nav progress bar
27991 * @param {Object} config The config object
27994 Roo.bootstrap.NavProgressBar = function(config){
27995 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27997 this.bullets = this.bullets || [];
27999 // Roo.bootstrap.NavProgressBar.register(this);
28003 * Fires when the active item changes
28004 * @param {Roo.bootstrap.NavProgressBar} this
28005 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28006 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
28013 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
28018 getAutoCreate : function()
28020 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28024 cls : 'roo-navigation-bar-group',
28028 cls : 'roo-navigation-top-bar'
28032 cls : 'roo-navigation-bullets-bar',
28036 cls : 'roo-navigation-bar'
28043 cls : 'roo-navigation-bottom-bar'
28053 initEvents: function()
28058 onRender : function(ct, position)
28060 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28062 if(this.bullets.length){
28063 Roo.each(this.bullets, function(b){
28072 addItem : function(cfg)
28074 var item = new Roo.bootstrap.NavProgressItem(cfg);
28076 item.parentId = this.id;
28077 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28080 var top = new Roo.bootstrap.Element({
28082 cls : 'roo-navigation-bar-text'
28085 var bottom = new Roo.bootstrap.Element({
28087 cls : 'roo-navigation-bar-text'
28090 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28091 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28093 var topText = new Roo.bootstrap.Element({
28095 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28098 var bottomText = new Roo.bootstrap.Element({
28100 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28103 topText.onRender(top.el, null);
28104 bottomText.onRender(bottom.el, null);
28107 item.bottomEl = bottom;
28110 this.barItems.push(item);
28115 getActive : function()
28117 var active = false;
28119 Roo.each(this.barItems, function(v){
28121 if (!v.isActive()) {
28133 setActiveItem : function(item)
28137 Roo.each(this.barItems, function(v){
28138 if (v.rid == item.rid) {
28142 if (v.isActive()) {
28143 v.setActive(false);
28148 item.setActive(true);
28150 this.fireEvent('changed', this, item, prev);
28153 getBarItem: function(rid)
28157 Roo.each(this.barItems, function(e) {
28158 if (e.rid != rid) {
28169 indexOfItem : function(item)
28173 Roo.each(this.barItems, function(v, i){
28175 if (v.rid != item.rid) {
28186 setActiveNext : function()
28188 var i = this.indexOfItem(this.getActive());
28190 if (i > this.barItems.length) {
28194 this.setActiveItem(this.barItems[i+1]);
28197 setActivePrev : function()
28199 var i = this.indexOfItem(this.getActive());
28205 this.setActiveItem(this.barItems[i-1]);
28208 format : function()
28210 if(!this.barItems.length){
28214 var width = 100 / this.barItems.length;
28216 Roo.each(this.barItems, function(i){
28217 i.el.setStyle('width', width + '%');
28218 i.topEl.el.setStyle('width', width + '%');
28219 i.bottomEl.el.setStyle('width', width + '%');
28228 * Nav Progress Item
28233 * @class Roo.bootstrap.NavProgressItem
28234 * @extends Roo.bootstrap.Component
28235 * Bootstrap NavProgressItem class
28236 * @cfg {String} rid the reference id
28237 * @cfg {Boolean} active (true|false) Is item active default false
28238 * @cfg {Boolean} disabled (true|false) Is item active default false
28239 * @cfg {String} html
28240 * @cfg {String} position (top|bottom) text position default bottom
28241 * @cfg {String} icon show icon instead of number
28244 * Create a new NavProgressItem
28245 * @param {Object} config The config object
28247 Roo.bootstrap.NavProgressItem = function(config){
28248 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28253 * The raw click event for the entire grid.
28254 * @param {Roo.bootstrap.NavProgressItem} this
28255 * @param {Roo.EventObject} e
28262 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
28268 position : 'bottom',
28271 getAutoCreate : function()
28273 var iconCls = 'roo-navigation-bar-item-icon';
28275 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28279 cls: 'roo-navigation-bar-item',
28289 cfg.cls += ' active';
28292 cfg.cls += ' disabled';
28298 disable : function()
28300 this.setDisabled(true);
28303 enable : function()
28305 this.setDisabled(false);
28308 initEvents: function()
28310 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28312 this.iconEl.on('click', this.onClick, this);
28315 onClick : function(e)
28317 e.preventDefault();
28323 if(this.fireEvent('click', this, e) === false){
28327 this.parent().setActiveItem(this);
28330 isActive: function ()
28332 return this.active;
28335 setActive : function(state)
28337 if(this.active == state){
28341 this.active = state;
28344 this.el.addClass('active');
28348 this.el.removeClass('active');
28353 setDisabled : function(state)
28355 if(this.disabled == state){
28359 this.disabled = state;
28362 this.el.addClass('disabled');
28366 this.el.removeClass('disabled');
28369 tooltipEl : function()
28371 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28384 * @class Roo.bootstrap.FieldLabel
28385 * @extends Roo.bootstrap.Component
28386 * Bootstrap FieldLabel class
28387 * @cfg {String} html contents of the element
28388 * @cfg {String} tag tag of the element default label
28389 * @cfg {String} cls class of the element
28390 * @cfg {String} target label target
28391 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28392 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28393 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28394 * @cfg {String} iconTooltip default "This field is required"
28397 * Create a new FieldLabel
28398 * @param {Object} config The config object
28401 Roo.bootstrap.FieldLabel = function(config){
28402 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28407 * Fires after the field has been marked as invalid.
28408 * @param {Roo.form.FieldLabel} this
28409 * @param {String} msg The validation message
28414 * Fires after the field has been validated with no errors.
28415 * @param {Roo.form.FieldLabel} this
28421 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28428 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28429 validClass : 'text-success fa fa-lg fa-check',
28430 iconTooltip : 'This field is required',
28432 getAutoCreate : function(){
28436 cls : 'roo-bootstrap-field-label ' + this.cls,
28442 tooltip : this.iconTooltip
28454 initEvents: function()
28456 Roo.bootstrap.Element.superclass.initEvents.call(this);
28458 this.iconEl = this.el.select('i', true).first();
28460 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28462 Roo.bootstrap.FieldLabel.register(this);
28466 * Mark this field as valid
28468 markValid : function()
28470 this.iconEl.show();
28472 this.iconEl.removeClass(this.invalidClass);
28474 this.iconEl.addClass(this.validClass);
28476 this.fireEvent('valid', this);
28480 * Mark this field as invalid
28481 * @param {String} msg The validation message
28483 markInvalid : function(msg)
28485 this.iconEl.show();
28487 this.iconEl.removeClass(this.validClass);
28489 this.iconEl.addClass(this.invalidClass);
28491 this.fireEvent('invalid', this, msg);
28497 Roo.apply(Roo.bootstrap.FieldLabel, {
28502 * register a FieldLabel Group
28503 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28505 register : function(label)
28507 if(this.groups.hasOwnProperty(label.target)){
28511 this.groups[label.target] = label;
28515 * fetch a FieldLabel Group based on the target
28516 * @param {string} target
28517 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28519 get: function(target) {
28520 if (typeof(this.groups[target]) == 'undefined') {
28524 return this.groups[target] ;
28533 * page DateSplitField.
28539 * @class Roo.bootstrap.DateSplitField
28540 * @extends Roo.bootstrap.Component
28541 * Bootstrap DateSplitField class
28542 * @cfg {string} fieldLabel - the label associated
28543 * @cfg {Number} labelWidth set the width of label (0-12)
28544 * @cfg {String} labelAlign (top|left)
28545 * @cfg {Boolean} dayAllowBlank (true|false) default false
28546 * @cfg {Boolean} monthAllowBlank (true|false) default false
28547 * @cfg {Boolean} yearAllowBlank (true|false) default false
28548 * @cfg {string} dayPlaceholder
28549 * @cfg {string} monthPlaceholder
28550 * @cfg {string} yearPlaceholder
28551 * @cfg {string} dayFormat default 'd'
28552 * @cfg {string} monthFormat default 'm'
28553 * @cfg {string} yearFormat default 'Y'
28557 * Create a new DateSplitField
28558 * @param {Object} config The config object
28561 Roo.bootstrap.DateSplitField = function(config){
28562 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28568 * getting the data of years
28569 * @param {Roo.bootstrap.DateSplitField} this
28570 * @param {Object} years
28575 * getting the data of days
28576 * @param {Roo.bootstrap.DateSplitField} this
28577 * @param {Object} days
28582 * Fires after the field has been marked as invalid.
28583 * @param {Roo.form.Field} this
28584 * @param {String} msg The validation message
28589 * Fires after the field has been validated with no errors.
28590 * @param {Roo.form.Field} this
28596 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28599 labelAlign : 'top',
28601 dayAllowBlank : false,
28602 monthAllowBlank : false,
28603 yearAllowBlank : false,
28604 dayPlaceholder : '',
28605 monthPlaceholder : '',
28606 yearPlaceholder : '',
28610 isFormField : true,
28612 getAutoCreate : function()
28616 cls : 'row roo-date-split-field-group',
28621 cls : 'form-hidden-field roo-date-split-field-group-value',
28627 if(this.fieldLabel){
28630 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28634 html : this.fieldLabel
28640 Roo.each(['day', 'month', 'year'], function(t){
28643 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28650 inputEl: function ()
28652 return this.el.select('.roo-date-split-field-group-value', true).first();
28655 onRender : function(ct, position)
28659 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28661 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28663 this.dayField = new Roo.bootstrap.ComboBox({
28664 allowBlank : this.dayAllowBlank,
28665 alwaysQuery : true,
28666 displayField : 'value',
28669 forceSelection : true,
28671 placeholder : this.dayPlaceholder,
28672 selectOnFocus : true,
28673 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28674 triggerAction : 'all',
28676 valueField : 'value',
28677 store : new Roo.data.SimpleStore({
28678 data : (function() {
28680 _this.fireEvent('days', _this, days);
28683 fields : [ 'value' ]
28686 select : function (_self, record, index)
28688 _this.setValue(_this.getValue());
28693 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28695 this.monthField = new Roo.bootstrap.MonthField({
28696 after : '<i class=\"fa fa-calendar\"></i>',
28697 allowBlank : this.monthAllowBlank,
28698 placeholder : this.monthPlaceholder,
28701 render : function (_self)
28703 this.el.select('span.input-group-addon', true).first().on('click', function(e){
28704 e.preventDefault();
28708 select : function (_self, oldvalue, newvalue)
28710 _this.setValue(_this.getValue());
28715 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28717 this.yearField = new Roo.bootstrap.ComboBox({
28718 allowBlank : this.yearAllowBlank,
28719 alwaysQuery : true,
28720 displayField : 'value',
28723 forceSelection : true,
28725 placeholder : this.yearPlaceholder,
28726 selectOnFocus : true,
28727 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28728 triggerAction : 'all',
28730 valueField : 'value',
28731 store : new Roo.data.SimpleStore({
28732 data : (function() {
28734 _this.fireEvent('years', _this, years);
28737 fields : [ 'value' ]
28740 select : function (_self, record, index)
28742 _this.setValue(_this.getValue());
28747 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28750 setValue : function(v, format)
28752 this.inputEl.dom.value = v;
28754 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28756 var d = Date.parseDate(v, f);
28763 this.setDay(d.format(this.dayFormat));
28764 this.setMonth(d.format(this.monthFormat));
28765 this.setYear(d.format(this.yearFormat));
28772 setDay : function(v)
28774 this.dayField.setValue(v);
28775 this.inputEl.dom.value = this.getValue();
28780 setMonth : function(v)
28782 this.monthField.setValue(v, true);
28783 this.inputEl.dom.value = this.getValue();
28788 setYear : function(v)
28790 this.yearField.setValue(v);
28791 this.inputEl.dom.value = this.getValue();
28796 getDay : function()
28798 return this.dayField.getValue();
28801 getMonth : function()
28803 return this.monthField.getValue();
28806 getYear : function()
28808 return this.yearField.getValue();
28811 getValue : function()
28813 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28815 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28825 this.inputEl.dom.value = '';
28830 validate : function()
28832 var d = this.dayField.validate();
28833 var m = this.monthField.validate();
28834 var y = this.yearField.validate();
28839 (!this.dayAllowBlank && !d) ||
28840 (!this.monthAllowBlank && !m) ||
28841 (!this.yearAllowBlank && !y)
28846 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28855 this.markInvalid();
28860 markValid : function()
28863 var label = this.el.select('label', true).first();
28864 var icon = this.el.select('i.fa-star', true).first();
28870 this.fireEvent('valid', this);
28874 * Mark this field as invalid
28875 * @param {String} msg The validation message
28877 markInvalid : function(msg)
28880 var label = this.el.select('label', true).first();
28881 var icon = this.el.select('i.fa-star', true).first();
28883 if(label && !icon){
28884 this.el.select('.roo-date-split-field-label', true).createChild({
28886 cls : 'text-danger fa fa-lg fa-star',
28887 tooltip : 'This field is required',
28888 style : 'margin-right:5px;'
28892 this.fireEvent('invalid', this, msg);
28895 clearInvalid : function()
28897 var label = this.el.select('label', true).first();
28898 var icon = this.el.select('i.fa-star', true).first();
28904 this.fireEvent('valid', this);
28907 getName: function()
28917 * http://masonry.desandro.com
28919 * The idea is to render all the bricks based on vertical width...
28921 * The original code extends 'outlayer' - we might need to use that....
28927 * @class Roo.bootstrap.LayoutMasonry
28928 * @extends Roo.bootstrap.Component
28929 * Bootstrap Layout Masonry class
28932 * Create a new Element
28933 * @param {Object} config The config object
28936 Roo.bootstrap.LayoutMasonry = function(config){
28937 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28943 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
28946 * @cfg {Boolean} isLayoutInstant = no animation?
28948 isLayoutInstant : false, // needed?
28951 * @cfg {Number} boxWidth width of the columns
28956 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
28961 * @cfg {Number} padWidth padding below box..
28966 * @cfg {Number} gutter gutter width..
28971 * @cfg {Number} maxCols maximum number of columns
28977 * @cfg {Boolean} isAutoInitial defalut true
28979 isAutoInitial : true,
28984 * @cfg {Boolean} isHorizontal defalut false
28986 isHorizontal : false,
28988 currentSize : null,
28994 bricks: null, //CompositeElement
28998 _isLayoutInited : false,
29000 // isAlternative : false, // only use for vertical layout...
29003 * @cfg {Number} alternativePadWidth padding below box..
29005 alternativePadWidth : 50,
29007 getAutoCreate : function(){
29011 cls: 'blog-masonary-wrapper ' + this.cls,
29013 cls : 'mas-boxes masonary'
29020 getChildContainer: function( )
29022 if (this.boxesEl) {
29023 return this.boxesEl;
29026 this.boxesEl = this.el.select('.mas-boxes').first();
29028 return this.boxesEl;
29032 initEvents : function()
29036 if(this.isAutoInitial){
29037 Roo.log('hook children rendered');
29038 this.on('childrenrendered', function() {
29039 Roo.log('children rendered');
29045 initial : function()
29047 this.currentSize = this.el.getBox(true);
29049 Roo.EventManager.onWindowResize(this.resize, this);
29051 if(!this.isAutoInitial){
29059 //this.layout.defer(500,this);
29063 resize : function()
29067 var cs = this.el.getBox(true);
29069 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29070 Roo.log("no change in with or X");
29074 this.currentSize = cs;
29080 layout : function()
29082 this._resetLayout();
29084 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29086 this.layoutItems( isInstant );
29088 this._isLayoutInited = true;
29092 _resetLayout : function()
29094 if(this.isHorizontal){
29095 this.horizontalMeasureColumns();
29099 this.verticalMeasureColumns();
29103 verticalMeasureColumns : function()
29105 this.getContainerWidth();
29107 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29108 // this.colWidth = Math.floor(this.containerWidth * 0.8);
29112 var boxWidth = this.boxWidth + this.padWidth;
29114 if(this.containerWidth < this.boxWidth){
29115 boxWidth = this.containerWidth
29118 var containerWidth = this.containerWidth;
29120 var cols = Math.floor(containerWidth / boxWidth);
29122 this.cols = Math.max( cols, 1 );
29124 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29126 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29128 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29130 this.colWidth = boxWidth + avail - this.padWidth;
29132 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29133 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
29136 horizontalMeasureColumns : function()
29138 this.getContainerWidth();
29140 var boxWidth = this.boxWidth;
29142 if(this.containerWidth < boxWidth){
29143 boxWidth = this.containerWidth;
29146 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29148 this.el.setHeight(boxWidth);
29152 getContainerWidth : function()
29154 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
29157 layoutItems : function( isInstant )
29159 var items = Roo.apply([], this.bricks);
29161 if(this.isHorizontal){
29162 this._horizontalLayoutItems( items , isInstant );
29166 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29167 // this._verticalAlternativeLayoutItems( items , isInstant );
29171 this._verticalLayoutItems( items , isInstant );
29175 _verticalLayoutItems : function ( items , isInstant)
29177 if ( !items || !items.length ) {
29182 ['xs', 'xs', 'xs', 'tall'],
29183 ['xs', 'xs', 'tall'],
29184 ['xs', 'xs', 'sm'],
29185 ['xs', 'xs', 'xs'],
29191 ['sm', 'xs', 'xs'],
29195 ['tall', 'xs', 'xs', 'xs'],
29196 ['tall', 'xs', 'xs'],
29208 Roo.each(items, function(item, k){
29210 switch (item.size) {
29211 // these layouts take up a full box,
29222 boxes.push([item]);
29245 var filterPattern = function(box, length)
29253 var pattern = box.slice(0, length);
29257 Roo.each(pattern, function(i){
29258 format.push(i.size);
29261 Roo.each(standard, function(s){
29263 if(String(s) != String(format)){
29272 if(!match && length == 1){
29277 filterPattern(box, length - 1);
29281 queue.push(pattern);
29283 box = box.slice(length, box.length);
29285 filterPattern(box, 4);
29291 Roo.each(boxes, function(box, k){
29297 if(box.length == 1){
29302 filterPattern(box, 4);
29306 this._processVerticalLayoutQueue( queue, isInstant );
29310 // _verticalAlternativeLayoutItems : function( items , isInstant )
29312 // if ( !items || !items.length ) {
29316 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
29320 _horizontalLayoutItems : function ( items , isInstant)
29322 if ( !items || !items.length || items.length < 3) {
29328 var eItems = items.slice(0, 3);
29330 items = items.slice(3, items.length);
29333 ['xs', 'xs', 'xs', 'wide'],
29334 ['xs', 'xs', 'wide'],
29335 ['xs', 'xs', 'sm'],
29336 ['xs', 'xs', 'xs'],
29342 ['sm', 'xs', 'xs'],
29346 ['wide', 'xs', 'xs', 'xs'],
29347 ['wide', 'xs', 'xs'],
29360 Roo.each(items, function(item, k){
29362 switch (item.size) {
29373 boxes.push([item]);
29397 var filterPattern = function(box, length)
29405 var pattern = box.slice(0, length);
29409 Roo.each(pattern, function(i){
29410 format.push(i.size);
29413 Roo.each(standard, function(s){
29415 if(String(s) != String(format)){
29424 if(!match && length == 1){
29429 filterPattern(box, length - 1);
29433 queue.push(pattern);
29435 box = box.slice(length, box.length);
29437 filterPattern(box, 4);
29443 Roo.each(boxes, function(box, k){
29449 if(box.length == 1){
29454 filterPattern(box, 4);
29461 var pos = this.el.getBox(true);
29465 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29467 var hit_end = false;
29469 Roo.each(queue, function(box){
29473 Roo.each(box, function(b){
29475 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29485 Roo.each(box, function(b){
29487 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29490 mx = Math.max(mx, b.x);
29494 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29498 Roo.each(box, function(b){
29500 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29514 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29517 /** Sets position of item in DOM
29518 * @param {Element} item
29519 * @param {Number} x - horizontal position
29520 * @param {Number} y - vertical position
29521 * @param {Boolean} isInstant - disables transitions
29523 _processVerticalLayoutQueue : function( queue, isInstant )
29525 var pos = this.el.getBox(true);
29530 for (var i = 0; i < this.cols; i++){
29534 Roo.each(queue, function(box, k){
29536 var col = k % this.cols;
29538 Roo.each(box, function(b,kk){
29540 b.el.position('absolute');
29542 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29543 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29545 if(b.size == 'md-left' || b.size == 'md-right'){
29546 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29547 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29550 b.el.setWidth(width);
29551 b.el.setHeight(height);
29553 b.el.select('iframe',true).setSize(width,height);
29557 for (var i = 0; i < this.cols; i++){
29559 if(maxY[i] < maxY[col]){
29564 col = Math.min(col, i);
29568 x = pos.x + col * (this.colWidth + this.padWidth);
29572 var positions = [];
29574 switch (box.length){
29576 positions = this.getVerticalOneBoxColPositions(x, y, box);
29579 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29582 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29585 positions = this.getVerticalFourBoxColPositions(x, y, box);
29591 Roo.each(box, function(b,kk){
29593 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29595 var sz = b.el.getSize();
29597 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29605 for (var i = 0; i < this.cols; i++){
29606 mY = Math.max(mY, maxY[i]);
29609 this.el.setHeight(mY - pos.y);
29613 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29615 // var pos = this.el.getBox(true);
29618 // var maxX = pos.right;
29620 // var maxHeight = 0;
29622 // Roo.each(items, function(item, k){
29626 // item.el.position('absolute');
29628 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29630 // item.el.setWidth(width);
29632 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29634 // item.el.setHeight(height);
29637 // item.el.setXY([x, y], isInstant ? false : true);
29639 // item.el.setXY([maxX - width, y], isInstant ? false : true);
29642 // y = y + height + this.alternativePadWidth;
29644 // maxHeight = maxHeight + height + this.alternativePadWidth;
29648 // this.el.setHeight(maxHeight);
29652 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29654 var pos = this.el.getBox(true);
29659 var maxX = pos.right;
29661 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29663 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29665 Roo.each(queue, function(box, k){
29667 Roo.each(box, function(b, kk){
29669 b.el.position('absolute');
29671 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29672 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29674 if(b.size == 'md-left' || b.size == 'md-right'){
29675 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29676 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29679 b.el.setWidth(width);
29680 b.el.setHeight(height);
29688 var positions = [];
29690 switch (box.length){
29692 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29695 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29698 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29701 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29707 Roo.each(box, function(b,kk){
29709 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29711 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29719 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29721 Roo.each(eItems, function(b,k){
29723 b.size = (k == 0) ? 'sm' : 'xs';
29724 b.x = (k == 0) ? 2 : 1;
29725 b.y = (k == 0) ? 2 : 1;
29727 b.el.position('absolute');
29729 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29731 b.el.setWidth(width);
29733 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29735 b.el.setHeight(height);
29739 var positions = [];
29742 x : maxX - this.unitWidth * 2 - this.gutter,
29747 x : maxX - this.unitWidth,
29748 y : minY + (this.unitWidth + this.gutter) * 2
29752 x : maxX - this.unitWidth * 3 - this.gutter * 2,
29756 Roo.each(eItems, function(b,k){
29758 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29764 getVerticalOneBoxColPositions : function(x, y, box)
29768 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29770 if(box[0].size == 'md-left'){
29774 if(box[0].size == 'md-right'){
29779 x : x + (this.unitWidth + this.gutter) * rand,
29786 getVerticalTwoBoxColPositions : function(x, y, box)
29790 if(box[0].size == 'xs'){
29794 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29798 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29812 x : x + (this.unitWidth + this.gutter) * 2,
29813 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29820 getVerticalThreeBoxColPositions : function(x, y, box)
29824 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29832 x : x + (this.unitWidth + this.gutter) * 1,
29837 x : x + (this.unitWidth + this.gutter) * 2,
29845 if(box[0].size == 'xs' && box[1].size == 'xs'){
29854 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29858 x : x + (this.unitWidth + this.gutter) * 1,
29872 x : x + (this.unitWidth + this.gutter) * 2,
29877 x : x + (this.unitWidth + this.gutter) * 2,
29878 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29885 getVerticalFourBoxColPositions : function(x, y, box)
29889 if(box[0].size == 'xs'){
29898 y : y + (this.unitHeight + this.gutter) * 1
29903 y : y + (this.unitHeight + this.gutter) * 2
29907 x : x + (this.unitWidth + this.gutter) * 1,
29921 x : x + (this.unitWidth + this.gutter) * 2,
29926 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29927 y : y + (this.unitHeight + this.gutter) * 1
29931 x : x + (this.unitWidth + this.gutter) * 2,
29932 y : y + (this.unitWidth + this.gutter) * 2
29939 getHorizontalOneBoxColPositions : function(maxX, minY, box)
29943 if(box[0].size == 'md-left'){
29945 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29952 if(box[0].size == 'md-right'){
29954 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29955 y : minY + (this.unitWidth + this.gutter) * 1
29961 var rand = Math.floor(Math.random() * (4 - box[0].y));
29964 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29965 y : minY + (this.unitWidth + this.gutter) * rand
29972 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29976 if(box[0].size == 'xs'){
29979 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29984 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29985 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29993 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29998 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29999 y : minY + (this.unitWidth + this.gutter) * 2
30006 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30010 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30013 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30018 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30019 y : minY + (this.unitWidth + this.gutter) * 1
30023 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30024 y : minY + (this.unitWidth + this.gutter) * 2
30031 if(box[0].size == 'xs' && box[1].size == 'xs'){
30034 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30039 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30044 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30045 y : minY + (this.unitWidth + this.gutter) * 1
30053 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30058 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30059 y : minY + (this.unitWidth + this.gutter) * 2
30063 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30064 y : minY + (this.unitWidth + this.gutter) * 2
30071 getHorizontalFourBoxColPositions : function(maxX, minY, box)
30075 if(box[0].size == 'xs'){
30078 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30083 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30088 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),
30093 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30094 y : minY + (this.unitWidth + this.gutter) * 1
30102 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30107 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30108 y : minY + (this.unitWidth + this.gutter) * 2
30112 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30113 y : minY + (this.unitWidth + this.gutter) * 2
30117 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),
30118 y : minY + (this.unitWidth + this.gutter) * 2
30132 * http://masonry.desandro.com
30134 * The idea is to render all the bricks based on vertical width...
30136 * The original code extends 'outlayer' - we might need to use that....
30142 * @class Roo.bootstrap.LayoutMasonryAuto
30143 * @extends Roo.bootstrap.Component
30144 * Bootstrap Layout Masonry class
30147 * Create a new Element
30148 * @param {Object} config The config object
30151 Roo.bootstrap.LayoutMasonryAuto = function(config){
30152 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30155 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
30158 * @cfg {Boolean} isFitWidth - resize the width..
30160 isFitWidth : false, // options..
30162 * @cfg {Boolean} isOriginLeft = left align?
30164 isOriginLeft : true,
30166 * @cfg {Boolean} isOriginTop = top align?
30168 isOriginTop : false,
30170 * @cfg {Boolean} isLayoutInstant = no animation?
30172 isLayoutInstant : false, // needed?
30174 * @cfg {Boolean} isResizingContainer = not sure if this is used..
30176 isResizingContainer : true,
30178 * @cfg {Number} columnWidth width of the columns
30184 * @cfg {Number} maxCols maximum number of columns
30189 * @cfg {Number} padHeight padding below box..
30195 * @cfg {Boolean} isAutoInitial defalut true
30198 isAutoInitial : true,
30204 initialColumnWidth : 0,
30205 currentSize : null,
30207 colYs : null, // array.
30214 bricks: null, //CompositeElement
30215 cols : 0, // array?
30216 // element : null, // wrapped now this.el
30217 _isLayoutInited : null,
30220 getAutoCreate : function(){
30224 cls: 'blog-masonary-wrapper ' + this.cls,
30226 cls : 'mas-boxes masonary'
30233 getChildContainer: function( )
30235 if (this.boxesEl) {
30236 return this.boxesEl;
30239 this.boxesEl = this.el.select('.mas-boxes').first();
30241 return this.boxesEl;
30245 initEvents : function()
30249 if(this.isAutoInitial){
30250 Roo.log('hook children rendered');
30251 this.on('childrenrendered', function() {
30252 Roo.log('children rendered');
30259 initial : function()
30261 this.reloadItems();
30263 this.currentSize = this.el.getBox(true);
30265 /// was window resize... - let's see if this works..
30266 Roo.EventManager.onWindowResize(this.resize, this);
30268 if(!this.isAutoInitial){
30273 this.layout.defer(500,this);
30276 reloadItems: function()
30278 this.bricks = this.el.select('.masonry-brick', true);
30280 this.bricks.each(function(b) {
30281 //Roo.log(b.getSize());
30282 if (!b.attr('originalwidth')) {
30283 b.attr('originalwidth', b.getSize().width);
30288 Roo.log(this.bricks.elements.length);
30291 resize : function()
30294 var cs = this.el.getBox(true);
30296 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30297 Roo.log("no change in with or X");
30300 this.currentSize = cs;
30304 layout : function()
30307 this._resetLayout();
30308 //this._manageStamps();
30310 // don't animate first layout
30311 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30312 this.layoutItems( isInstant );
30314 // flag for initalized
30315 this._isLayoutInited = true;
30318 layoutItems : function( isInstant )
30320 //var items = this._getItemsForLayout( this.items );
30321 // original code supports filtering layout items.. we just ignore it..
30323 this._layoutItems( this.bricks , isInstant );
30325 this._postLayout();
30327 _layoutItems : function ( items , isInstant)
30329 //this.fireEvent( 'layout', this, items );
30332 if ( !items || !items.elements.length ) {
30333 // no items, emit event with empty array
30338 items.each(function(item) {
30339 Roo.log("layout item");
30341 // get x/y object from method
30342 var position = this._getItemLayoutPosition( item );
30344 position.item = item;
30345 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30346 queue.push( position );
30349 this._processLayoutQueue( queue );
30351 /** Sets position of item in DOM
30352 * @param {Element} item
30353 * @param {Number} x - horizontal position
30354 * @param {Number} y - vertical position
30355 * @param {Boolean} isInstant - disables transitions
30357 _processLayoutQueue : function( queue )
30359 for ( var i=0, len = queue.length; i < len; i++ ) {
30360 var obj = queue[i];
30361 obj.item.position('absolute');
30362 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30368 * Any logic you want to do after each layout,
30369 * i.e. size the container
30371 _postLayout : function()
30373 this.resizeContainer();
30376 resizeContainer : function()
30378 if ( !this.isResizingContainer ) {
30381 var size = this._getContainerSize();
30383 this.el.setSize(size.width,size.height);
30384 this.boxesEl.setSize(size.width,size.height);
30390 _resetLayout : function()
30392 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30393 this.colWidth = this.el.getWidth();
30394 //this.gutter = this.el.getWidth();
30396 this.measureColumns();
30402 this.colYs.push( 0 );
30408 measureColumns : function()
30410 this.getContainerWidth();
30411 // if columnWidth is 0, default to outerWidth of first item
30412 if ( !this.columnWidth ) {
30413 var firstItem = this.bricks.first();
30414 Roo.log(firstItem);
30415 this.columnWidth = this.containerWidth;
30416 if (firstItem && firstItem.attr('originalwidth') ) {
30417 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30419 // columnWidth fall back to item of first element
30420 Roo.log("set column width?");
30421 this.initialColumnWidth = this.columnWidth ;
30423 // if first elem has no width, default to size of container
30428 if (this.initialColumnWidth) {
30429 this.columnWidth = this.initialColumnWidth;
30434 // column width is fixed at the top - however if container width get's smaller we should
30437 // this bit calcs how man columns..
30439 var columnWidth = this.columnWidth += this.gutter;
30441 // calculate columns
30442 var containerWidth = this.containerWidth + this.gutter;
30444 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30445 // fix rounding errors, typically with gutters
30446 var excess = columnWidth - containerWidth % columnWidth;
30449 // if overshoot is less than a pixel, round up, otherwise floor it
30450 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30451 cols = Math[ mathMethod ]( cols );
30452 this.cols = Math.max( cols, 1 );
30453 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30455 // padding positioning..
30456 var totalColWidth = this.cols * this.columnWidth;
30457 var padavail = this.containerWidth - totalColWidth;
30458 // so for 2 columns - we need 3 'pads'
30460 var padNeeded = (1+this.cols) * this.padWidth;
30462 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30464 this.columnWidth += padExtra
30465 //this.padWidth = Math.floor(padavail / ( this.cols));
30467 // adjust colum width so that padding is fixed??
30469 // we have 3 columns ... total = width * 3
30470 // we have X left over... that should be used by
30472 //if (this.expandC) {
30480 getContainerWidth : function()
30482 /* // container is parent if fit width
30483 var container = this.isFitWidth ? this.element.parentNode : this.element;
30484 // check that this.size and size are there
30485 // IE8 triggers resize on body size change, so they might not be
30487 var size = getSize( container ); //FIXME
30488 this.containerWidth = size && size.innerWidth; //FIXME
30491 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30495 _getItemLayoutPosition : function( item ) // what is item?
30497 // we resize the item to our columnWidth..
30499 item.setWidth(this.columnWidth);
30500 item.autoBoxAdjust = false;
30502 var sz = item.getSize();
30504 // how many columns does this brick span
30505 var remainder = this.containerWidth % this.columnWidth;
30507 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30508 // round if off by 1 pixel, otherwise use ceil
30509 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30510 colSpan = Math.min( colSpan, this.cols );
30512 // normally this should be '1' as we dont' currently allow multi width columns..
30514 var colGroup = this._getColGroup( colSpan );
30515 // get the minimum Y value from the columns
30516 var minimumY = Math.min.apply( Math, colGroup );
30517 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30519 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30521 // position the brick
30523 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30524 y: this.currentSize.y + minimumY + this.padHeight
30528 // apply setHeight to necessary columns
30529 var setHeight = minimumY + sz.height + this.padHeight;
30530 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30532 var setSpan = this.cols + 1 - colGroup.length;
30533 for ( var i = 0; i < setSpan; i++ ) {
30534 this.colYs[ shortColIndex + i ] = setHeight ;
30541 * @param {Number} colSpan - number of columns the element spans
30542 * @returns {Array} colGroup
30544 _getColGroup : function( colSpan )
30546 if ( colSpan < 2 ) {
30547 // if brick spans only one column, use all the column Ys
30552 // how many different places could this brick fit horizontally
30553 var groupCount = this.cols + 1 - colSpan;
30554 // for each group potential horizontal position
30555 for ( var i = 0; i < groupCount; i++ ) {
30556 // make an array of colY values for that one group
30557 var groupColYs = this.colYs.slice( i, i + colSpan );
30558 // and get the max value of the array
30559 colGroup[i] = Math.max.apply( Math, groupColYs );
30564 _manageStamp : function( stamp )
30566 var stampSize = stamp.getSize();
30567 var offset = stamp.getBox();
30568 // get the columns that this stamp affects
30569 var firstX = this.isOriginLeft ? offset.x : offset.right;
30570 var lastX = firstX + stampSize.width;
30571 var firstCol = Math.floor( firstX / this.columnWidth );
30572 firstCol = Math.max( 0, firstCol );
30574 var lastCol = Math.floor( lastX / this.columnWidth );
30575 // lastCol should not go over if multiple of columnWidth #425
30576 lastCol -= lastX % this.columnWidth ? 0 : 1;
30577 lastCol = Math.min( this.cols - 1, lastCol );
30579 // set colYs to bottom of the stamp
30580 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30583 for ( var i = firstCol; i <= lastCol; i++ ) {
30584 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30589 _getContainerSize : function()
30591 this.maxY = Math.max.apply( Math, this.colYs );
30596 if ( this.isFitWidth ) {
30597 size.width = this._getContainerFitWidth();
30603 _getContainerFitWidth : function()
30605 var unusedCols = 0;
30606 // count unused columns
30609 if ( this.colYs[i] !== 0 ) {
30614 // fit container to columns that have been used
30615 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30618 needsResizeLayout : function()
30620 var previousWidth = this.containerWidth;
30621 this.getContainerWidth();
30622 return previousWidth !== this.containerWidth;
30637 * @class Roo.bootstrap.MasonryBrick
30638 * @extends Roo.bootstrap.Component
30639 * Bootstrap MasonryBrick class
30642 * Create a new MasonryBrick
30643 * @param {Object} config The config object
30646 Roo.bootstrap.MasonryBrick = function(config){
30647 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30653 * When a MasonryBrick is clcik
30654 * @param {Roo.bootstrap.MasonryBrick} this
30655 * @param {Roo.EventObject} e
30661 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
30664 * @cfg {String} title
30668 * @cfg {String} html
30672 * @cfg {String} bgimage
30676 * @cfg {String} videourl
30680 * @cfg {String} cls
30684 * @cfg {String} href
30688 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30693 * @cfg {String} (center|bottom) placetitle
30697 getAutoCreate : function()
30699 var cls = 'masonry-brick';
30701 if(this.href.length){
30702 cls += ' masonry-brick-link';
30705 if(this.bgimage.length){
30706 cls += ' masonry-brick-image';
30710 cls += ' masonry-' + this.size + '-brick';
30713 if(this.placetitle.length){
30715 switch (this.placetitle) {
30717 cls += ' masonry-center-title';
30720 cls += ' masonry-bottom-title';
30727 if(!this.html.length && !this.bgimage.length){
30728 cls += ' masonry-center-title';
30731 if(!this.html.length && this.bgimage.length){
30732 cls += ' masonry-bottom-title';
30737 cls += ' ' + this.cls;
30741 tag: (this.href.length) ? 'a' : 'div',
30746 cls: 'masonry-brick-paragraph',
30752 if(this.href.length){
30753 cfg.href = this.href;
30756 var cn = cfg.cn[0].cn;
30758 if(this.title.length){
30761 cls: 'masonry-brick-title',
30766 if(this.html.length){
30769 cls: 'masonry-brick-text',
30773 if (!this.title.length && !this.html.length) {
30774 cfg.cn[0].cls += ' hide';
30777 if(this.bgimage.length){
30780 cls: 'masonry-brick-image-view',
30784 if(this.videourl.length){
30785 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30786 // youtube support only?
30789 cls: 'masonry-brick-image-view',
30792 allowfullscreen : true
30801 initEvents: function()
30803 switch (this.size) {
30805 // this.intSize = 1;
30810 // this.intSize = 2;
30817 // this.intSize = 3;
30822 // this.intSize = 3;
30827 // this.intSize = 3;
30832 // this.intSize = 3;
30844 this.el.on('touchstart', this.onTouchStart, this);
30845 this.el.on('touchmove', this.onTouchMove, this);
30846 this.el.on('touchend', this.onTouchEnd, this);
30847 this.el.on('contextmenu', this.onContextMenu, this);
30849 this.el.on('mouseenter' ,this.enter, this);
30850 this.el.on('mouseleave', this.leave, this);
30853 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30854 this.parent().bricks.push(this);
30859 onClick: function(e, el)
30865 var time = this.endTimer - this.startTimer;
30873 e.preventDefault();
30876 enter: function(e, el)
30878 e.preventDefault();
30880 if(this.bgimage.length && this.html.length){
30881 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30885 leave: function(e, el)
30887 e.preventDefault();
30889 if(this.bgimage.length && this.html.length){
30890 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30894 onTouchStart: function(e, el)
30896 // e.preventDefault();
30898 this.touchmoved = false;
30900 if(!this.bgimage.length || !this.html.length){
30904 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30906 this.timer = new Date().getTime();
30910 onTouchMove: function(e, el)
30912 this.touchmoved = true;
30915 onContextMenu : function(e,el)
30917 e.preventDefault();
30918 e.stopPropagation();
30922 onTouchEnd: function(e, el)
30924 // e.preventDefault();
30926 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30933 if(!this.bgimage.length || !this.html.length){
30935 if(this.href.length){
30936 window.location.href = this.href;
30942 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30944 window.location.href = this.href;
30959 * @class Roo.bootstrap.Brick
30960 * @extends Roo.bootstrap.Component
30961 * Bootstrap Brick class
30964 * Create a new Brick
30965 * @param {Object} config The config object
30968 Roo.bootstrap.Brick = function(config){
30969 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30975 * When a Brick is click
30976 * @param {Roo.bootstrap.Brick} this
30977 * @param {Roo.EventObject} e
30983 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
30986 * @cfg {String} title
30990 * @cfg {String} html
30994 * @cfg {String} bgimage
30998 * @cfg {String} cls
31002 * @cfg {String} href
31006 * @cfg {String} video
31010 * @cfg {Boolean} square
31014 getAutoCreate : function()
31016 var cls = 'roo-brick';
31018 if(this.href.length){
31019 cls += ' roo-brick-link';
31022 if(this.bgimage.length){
31023 cls += ' roo-brick-image';
31026 if(!this.html.length && !this.bgimage.length){
31027 cls += ' roo-brick-center-title';
31030 if(!this.html.length && this.bgimage.length){
31031 cls += ' roo-brick-bottom-title';
31035 cls += ' ' + this.cls;
31039 tag: (this.href.length) ? 'a' : 'div',
31044 cls: 'roo-brick-paragraph',
31050 if(this.href.length){
31051 cfg.href = this.href;
31054 var cn = cfg.cn[0].cn;
31056 if(this.title.length){
31059 cls: 'roo-brick-title',
31064 if(this.html.length){
31067 cls: 'roo-brick-text',
31074 if(this.bgimage.length){
31077 cls: 'roo-brick-image-view',
31085 initEvents: function()
31087 if(this.title.length || this.html.length){
31088 this.el.on('mouseenter' ,this.enter, this);
31089 this.el.on('mouseleave', this.leave, this);
31093 Roo.EventManager.onWindowResize(this.resize, this);
31098 resize : function()
31100 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31102 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31103 // paragraph.setHeight(paragraph.getWidth());
31105 if(this.bgimage.length){
31106 var image = this.el.select('.roo-brick-image-view', true).first();
31107 image.setWidth(paragraph.getWidth());
31108 image.setHeight(paragraph.getWidth());
31113 enter: function(e, el)
31115 e.preventDefault();
31117 if(this.bgimage.length){
31118 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31119 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31123 leave: function(e, el)
31125 e.preventDefault();
31127 if(this.bgimage.length){
31128 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31129 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31139 * Ext JS Library 1.1.1
31140 * Copyright(c) 2006-2007, Ext JS, LLC.
31142 * Originally Released Under LGPL - original licence link has changed is not relivant.
31145 * <script type="text/javascript">
31150 * @class Roo.bootstrap.SplitBar
31151 * @extends Roo.util.Observable
31152 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31156 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31157 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31158 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31159 split.minSize = 100;
31160 split.maxSize = 600;
31161 split.animate = true;
31162 split.on('moved', splitterMoved);
31165 * Create a new SplitBar
31166 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
31167 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
31168 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31169 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
31170 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31171 position of the SplitBar).
31173 Roo.bootstrap.SplitBar = function(cfg){
31178 // dragElement : elm
31179 // resizingElement: el,
31181 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31182 // placement : Roo.bootstrap.SplitBar.LEFT ,
31183 // existingProxy ???
31186 this.el = Roo.get(cfg.dragElement, true);
31187 this.el.dom.unselectable = "on";
31189 this.resizingEl = Roo.get(cfg.resizingElement, true);
31193 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31194 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31197 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31200 * The minimum size of the resizing element. (Defaults to 0)
31206 * The maximum size of the resizing element. (Defaults to 2000)
31209 this.maxSize = 2000;
31212 * Whether to animate the transition to the new size
31215 this.animate = false;
31218 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31221 this.useShim = false;
31226 if(!cfg.existingProxy){
31228 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31230 this.proxy = Roo.get(cfg.existingProxy).dom;
31233 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31236 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31239 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31242 this.dragSpecs = {};
31245 * @private The adapter to use to positon and resize elements
31247 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31248 this.adapter.init(this);
31250 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31252 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31253 this.el.addClass("roo-splitbar-h");
31256 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31257 this.el.addClass("roo-splitbar-v");
31263 * Fires when the splitter is moved (alias for {@link #event-moved})
31264 * @param {Roo.bootstrap.SplitBar} this
31265 * @param {Number} newSize the new width or height
31270 * Fires when the splitter is moved
31271 * @param {Roo.bootstrap.SplitBar} this
31272 * @param {Number} newSize the new width or height
31276 * @event beforeresize
31277 * Fires before the splitter is dragged
31278 * @param {Roo.bootstrap.SplitBar} this
31280 "beforeresize" : true,
31282 "beforeapply" : true
31285 Roo.util.Observable.call(this);
31288 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31289 onStartProxyDrag : function(x, y){
31290 this.fireEvent("beforeresize", this);
31292 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
31294 o.enableDisplayMode("block");
31295 // all splitbars share the same overlay
31296 Roo.bootstrap.SplitBar.prototype.overlay = o;
31298 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31299 this.overlay.show();
31300 Roo.get(this.proxy).setDisplayed("block");
31301 var size = this.adapter.getElementSize(this);
31302 this.activeMinSize = this.getMinimumSize();;
31303 this.activeMaxSize = this.getMaximumSize();;
31304 var c1 = size - this.activeMinSize;
31305 var c2 = Math.max(this.activeMaxSize - size, 0);
31306 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31307 this.dd.resetConstraints();
31308 this.dd.setXConstraint(
31309 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
31310 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31312 this.dd.setYConstraint(0, 0);
31314 this.dd.resetConstraints();
31315 this.dd.setXConstraint(0, 0);
31316 this.dd.setYConstraint(
31317 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
31318 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31321 this.dragSpecs.startSize = size;
31322 this.dragSpecs.startPoint = [x, y];
31323 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31327 * @private Called after the drag operation by the DDProxy
31329 onEndProxyDrag : function(e){
31330 Roo.get(this.proxy).setDisplayed(false);
31331 var endPoint = Roo.lib.Event.getXY(e);
31333 this.overlay.hide();
31336 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31337 newSize = this.dragSpecs.startSize +
31338 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31339 endPoint[0] - this.dragSpecs.startPoint[0] :
31340 this.dragSpecs.startPoint[0] - endPoint[0]
31343 newSize = this.dragSpecs.startSize +
31344 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31345 endPoint[1] - this.dragSpecs.startPoint[1] :
31346 this.dragSpecs.startPoint[1] - endPoint[1]
31349 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31350 if(newSize != this.dragSpecs.startSize){
31351 if(this.fireEvent('beforeapply', this, newSize) !== false){
31352 this.adapter.setElementSize(this, newSize);
31353 this.fireEvent("moved", this, newSize);
31354 this.fireEvent("resize", this, newSize);
31360 * Get the adapter this SplitBar uses
31361 * @return The adapter object
31363 getAdapter : function(){
31364 return this.adapter;
31368 * Set the adapter this SplitBar uses
31369 * @param {Object} adapter A SplitBar adapter object
31371 setAdapter : function(adapter){
31372 this.adapter = adapter;
31373 this.adapter.init(this);
31377 * Gets the minimum size for the resizing element
31378 * @return {Number} The minimum size
31380 getMinimumSize : function(){
31381 return this.minSize;
31385 * Sets the minimum size for the resizing element
31386 * @param {Number} minSize The minimum size
31388 setMinimumSize : function(minSize){
31389 this.minSize = minSize;
31393 * Gets the maximum size for the resizing element
31394 * @return {Number} The maximum size
31396 getMaximumSize : function(){
31397 return this.maxSize;
31401 * Sets the maximum size for the resizing element
31402 * @param {Number} maxSize The maximum size
31404 setMaximumSize : function(maxSize){
31405 this.maxSize = maxSize;
31409 * Sets the initialize size for the resizing element
31410 * @param {Number} size The initial size
31412 setCurrentSize : function(size){
31413 var oldAnimate = this.animate;
31414 this.animate = false;
31415 this.adapter.setElementSize(this, size);
31416 this.animate = oldAnimate;
31420 * Destroy this splitbar.
31421 * @param {Boolean} removeEl True to remove the element
31423 destroy : function(removeEl){
31425 this.shim.remove();
31428 this.proxy.parentNode.removeChild(this.proxy);
31436 * @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.
31438 Roo.bootstrap.SplitBar.createProxy = function(dir){
31439 var proxy = new Roo.Element(document.createElement("div"));
31440 proxy.unselectable();
31441 var cls = 'roo-splitbar-proxy';
31442 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31443 document.body.appendChild(proxy.dom);
31448 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31449 * Default Adapter. It assumes the splitter and resizing element are not positioned
31450 * elements and only gets/sets the width of the element. Generally used for table based layouts.
31452 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31455 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31456 // do nothing for now
31457 init : function(s){
31461 * Called before drag operations to get the current size of the resizing element.
31462 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31464 getElementSize : function(s){
31465 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31466 return s.resizingEl.getWidth();
31468 return s.resizingEl.getHeight();
31473 * Called after drag operations to set the size of the resizing element.
31474 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31475 * @param {Number} newSize The new size to set
31476 * @param {Function} onComplete A function to be invoked when resizing is complete
31478 setElementSize : function(s, newSize, onComplete){
31479 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31481 s.resizingEl.setWidth(newSize);
31483 onComplete(s, newSize);
31486 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31491 s.resizingEl.setHeight(newSize);
31493 onComplete(s, newSize);
31496 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31503 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31504 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31505 * Adapter that moves the splitter element to align with the resized sizing element.
31506 * Used with an absolute positioned SplitBar.
31507 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31508 * document.body, make sure you assign an id to the body element.
31510 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31511 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31512 this.container = Roo.get(container);
31515 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31516 init : function(s){
31517 this.basic.init(s);
31520 getElementSize : function(s){
31521 return this.basic.getElementSize(s);
31524 setElementSize : function(s, newSize, onComplete){
31525 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31528 moveSplitter : function(s){
31529 var yes = Roo.bootstrap.SplitBar;
31530 switch(s.placement){
31532 s.el.setX(s.resizingEl.getRight());
31535 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31538 s.el.setY(s.resizingEl.getBottom());
31541 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31548 * Orientation constant - Create a vertical SplitBar
31552 Roo.bootstrap.SplitBar.VERTICAL = 1;
31555 * Orientation constant - Create a horizontal SplitBar
31559 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31562 * Placement constant - The resizing element is to the left of the splitter element
31566 Roo.bootstrap.SplitBar.LEFT = 1;
31569 * Placement constant - The resizing element is to the right of the splitter element
31573 Roo.bootstrap.SplitBar.RIGHT = 2;
31576 * Placement constant - The resizing element is positioned above the splitter element
31580 Roo.bootstrap.SplitBar.TOP = 3;
31583 * Placement constant - The resizing element is positioned under splitter element
31587 Roo.bootstrap.SplitBar.BOTTOM = 4;
31588 Roo.namespace("Roo.bootstrap.layout");/*
31590 * Ext JS Library 1.1.1
31591 * Copyright(c) 2006-2007, Ext JS, LLC.
31593 * Originally Released Under LGPL - original licence link has changed is not relivant.
31596 * <script type="text/javascript">
31600 * @class Roo.bootstrap.layout.Manager
31601 * @extends Roo.bootstrap.Component
31602 * Base class for layout managers.
31604 Roo.bootstrap.layout.Manager = function(config)
31606 Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31612 /** false to disable window resize monitoring @type Boolean */
31613 this.monitorWindowResize = true;
31618 * Fires when a layout is performed.
31619 * @param {Roo.LayoutManager} this
31623 * @event regionresized
31624 * Fires when the user resizes a region.
31625 * @param {Roo.LayoutRegion} region The resized region
31626 * @param {Number} newSize The new size (width for east/west, height for north/south)
31628 "regionresized" : true,
31630 * @event regioncollapsed
31631 * Fires when a region is collapsed.
31632 * @param {Roo.LayoutRegion} region The collapsed region
31634 "regioncollapsed" : true,
31636 * @event regionexpanded
31637 * Fires when a region is expanded.
31638 * @param {Roo.LayoutRegion} region The expanded region
31640 "regionexpanded" : true
31642 this.updating = false;
31645 this.el = Roo.get(config.el);
31651 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31656 monitorWindowResize : true,
31662 onRender : function(ct, position)
31665 this.el = Roo.get(ct);
31671 initEvents: function()
31675 // ie scrollbar fix
31676 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31677 document.body.scroll = "no";
31678 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31679 this.el.position('relative');
31681 this.id = this.el.id;
31682 this.el.addClass("roo-layout-container");
31683 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31684 if(this.el.dom != document.body ) {
31685 this.el.on('resize', this.layout,this);
31686 this.el.on('show', this.layout,this);
31692 * Returns true if this layout is currently being updated
31693 * @return {Boolean}
31695 isUpdating : function(){
31696 return this.updating;
31700 * Suspend the LayoutManager from doing auto-layouts while
31701 * making multiple add or remove calls
31703 beginUpdate : function(){
31704 this.updating = true;
31708 * Restore auto-layouts and optionally disable the manager from performing a layout
31709 * @param {Boolean} noLayout true to disable a layout update
31711 endUpdate : function(noLayout){
31712 this.updating = false;
31718 layout: function(){
31722 onRegionResized : function(region, newSize){
31723 this.fireEvent("regionresized", region, newSize);
31727 onRegionCollapsed : function(region){
31728 this.fireEvent("regioncollapsed", region);
31731 onRegionExpanded : function(region){
31732 this.fireEvent("regionexpanded", region);
31736 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31737 * performs box-model adjustments.
31738 * @return {Object} The size as an object {width: (the width), height: (the height)}
31740 getViewSize : function()
31743 if(this.el.dom != document.body){
31744 size = this.el.getSize();
31746 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31748 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31749 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31754 * Returns the Element this layout is bound to.
31755 * @return {Roo.Element}
31757 getEl : function(){
31762 * Returns the specified region.
31763 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31764 * @return {Roo.LayoutRegion}
31766 getRegion : function(target){
31767 return this.regions[target.toLowerCase()];
31770 onWindowResize : function(){
31771 if(this.monitorWindowResize){
31777 * Ext JS Library 1.1.1
31778 * Copyright(c) 2006-2007, Ext JS, LLC.
31780 * Originally Released Under LGPL - original licence link has changed is not relivant.
31783 * <script type="text/javascript">
31786 * @class Roo.bootstrap.layout.Border
31787 * @extends Roo.bootstrap.layout.Manager
31788 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31789 * please see: examples/bootstrap/nested.html<br><br>
31791 <b>The container the layout is rendered into can be either the body element or any other element.
31792 If it is not the body element, the container needs to either be an absolute positioned element,
31793 or you will need to add "position:relative" to the css of the container. You will also need to specify
31794 the container size if it is not the body element.</b>
31797 * Create a new Border
31798 * @param {Object} config Configuration options
31800 Roo.bootstrap.layout.Border = function(config){
31801 config = config || {};
31802 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31806 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31807 if(config[region]){
31808 config[region].region = region;
31809 this.addRegion(config[region]);
31815 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
31817 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31819 * Creates and adds a new region if it doesn't already exist.
31820 * @param {String} target The target region key (north, south, east, west or center).
31821 * @param {Object} config The regions config object
31822 * @return {BorderLayoutRegion} The new region
31824 addRegion : function(config)
31826 if(!this.regions[config.region]){
31827 var r = this.factory(config);
31828 this.bindRegion(r);
31830 return this.regions[config.region];
31834 bindRegion : function(r){
31835 this.regions[r.config.region] = r;
31837 r.on("visibilitychange", this.layout, this);
31838 r.on("paneladded", this.layout, this);
31839 r.on("panelremoved", this.layout, this);
31840 r.on("invalidated", this.layout, this);
31841 r.on("resized", this.onRegionResized, this);
31842 r.on("collapsed", this.onRegionCollapsed, this);
31843 r.on("expanded", this.onRegionExpanded, this);
31847 * Performs a layout update.
31849 layout : function()
31851 if(this.updating) {
31855 // render all the rebions if they have not been done alreayd?
31856 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31857 if(this.regions[region] && !this.regions[region].bodyEl){
31858 this.regions[region].onRender(this.el)
31862 var size = this.getViewSize();
31863 var w = size.width;
31864 var h = size.height;
31869 //var x = 0, y = 0;
31871 var rs = this.regions;
31872 var north = rs["north"];
31873 var south = rs["south"];
31874 var west = rs["west"];
31875 var east = rs["east"];
31876 var center = rs["center"];
31877 //if(this.hideOnLayout){ // not supported anymore
31878 //c.el.setStyle("display", "none");
31880 if(north && north.isVisible()){
31881 var b = north.getBox();
31882 var m = north.getMargins();
31883 b.width = w - (m.left+m.right);
31886 centerY = b.height + b.y + m.bottom;
31887 centerH -= centerY;
31888 north.updateBox(this.safeBox(b));
31890 if(south && south.isVisible()){
31891 var b = south.getBox();
31892 var m = south.getMargins();
31893 b.width = w - (m.left+m.right);
31895 var totalHeight = (b.height + m.top + m.bottom);
31896 b.y = h - totalHeight + m.top;
31897 centerH -= totalHeight;
31898 south.updateBox(this.safeBox(b));
31900 if(west && west.isVisible()){
31901 var b = west.getBox();
31902 var m = west.getMargins();
31903 b.height = centerH - (m.top+m.bottom);
31905 b.y = centerY + m.top;
31906 var totalWidth = (b.width + m.left + m.right);
31907 centerX += totalWidth;
31908 centerW -= totalWidth;
31909 west.updateBox(this.safeBox(b));
31911 if(east && east.isVisible()){
31912 var b = east.getBox();
31913 var m = east.getMargins();
31914 b.height = centerH - (m.top+m.bottom);
31915 var totalWidth = (b.width + m.left + m.right);
31916 b.x = w - totalWidth + m.left;
31917 b.y = centerY + m.top;
31918 centerW -= totalWidth;
31919 east.updateBox(this.safeBox(b));
31922 var m = center.getMargins();
31924 x: centerX + m.left,
31925 y: centerY + m.top,
31926 width: centerW - (m.left+m.right),
31927 height: centerH - (m.top+m.bottom)
31929 //if(this.hideOnLayout){
31930 //center.el.setStyle("display", "block");
31932 center.updateBox(this.safeBox(centerBox));
31935 this.fireEvent("layout", this);
31939 safeBox : function(box){
31940 box.width = Math.max(0, box.width);
31941 box.height = Math.max(0, box.height);
31946 * Adds a ContentPanel (or subclass) to this layout.
31947 * @param {String} target The target region key (north, south, east, west or center).
31948 * @param {Roo.ContentPanel} panel The panel to add
31949 * @return {Roo.ContentPanel} The added panel
31951 add : function(target, panel){
31953 target = target.toLowerCase();
31954 return this.regions[target].add(panel);
31958 * Remove a ContentPanel (or subclass) to this layout.
31959 * @param {String} target The target region key (north, south, east, west or center).
31960 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31961 * @return {Roo.ContentPanel} The removed panel
31963 remove : function(target, panel){
31964 target = target.toLowerCase();
31965 return this.regions[target].remove(panel);
31969 * Searches all regions for a panel with the specified id
31970 * @param {String} panelId
31971 * @return {Roo.ContentPanel} The panel or null if it wasn't found
31973 findPanel : function(panelId){
31974 var rs = this.regions;
31975 for(var target in rs){
31976 if(typeof rs[target] != "function"){
31977 var p = rs[target].getPanel(panelId);
31987 * Searches all regions for a panel with the specified id and activates (shows) it.
31988 * @param {String/ContentPanel} panelId The panels id or the panel itself
31989 * @return {Roo.ContentPanel} The shown panel or null
31991 showPanel : function(panelId) {
31992 var rs = this.regions;
31993 for(var target in rs){
31994 var r = rs[target];
31995 if(typeof r != "function"){
31996 if(r.hasPanel(panelId)){
31997 return r.showPanel(panelId);
32005 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32006 * @param {Roo.state.Provider} provider (optional) An alternate state provider
32009 restoreState : function(provider){
32011 provider = Roo.state.Manager;
32013 var sm = new Roo.LayoutStateManager();
32014 sm.init(this, provider);
32020 * Adds a xtype elements to the layout.
32024 xtype : 'ContentPanel',
32031 xtype : 'NestedLayoutPanel',
32037 items : [ ... list of content panels or nested layout panels.. ]
32041 * @param {Object} cfg Xtype definition of item to add.
32043 addxtype : function(cfg)
32045 // basically accepts a pannel...
32046 // can accept a layout region..!?!?
32047 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32050 // theory? children can only be panels??
32052 //if (!cfg.xtype.match(/Panel$/)) {
32057 if (typeof(cfg.region) == 'undefined') {
32058 Roo.log("Failed to add Panel, region was not set");
32062 var region = cfg.region;
32068 xitems = cfg.items;
32075 case 'Content': // ContentPanel (el, cfg)
32076 case 'Scroll': // ContentPanel (el, cfg)
32078 cfg.autoCreate = true;
32079 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32081 // var el = this.el.createChild();
32082 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32085 this.add(region, ret);
32089 case 'TreePanel': // our new panel!
32090 cfg.el = this.el.createChild();
32091 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32092 this.add(region, ret);
32097 // create a new Layout (which is a Border Layout...
32099 var clayout = cfg.layout;
32100 clayout.el = this.el.createChild();
32101 clayout.items = clayout.items || [];
32105 // replace this exitems with the clayout ones..
32106 xitems = clayout.items;
32108 // force background off if it's in center...
32109 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32110 cfg.background = false;
32112 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
32115 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32116 //console.log('adding nested layout panel ' + cfg.toSource());
32117 this.add(region, ret);
32118 nb = {}; /// find first...
32123 // needs grid and region
32125 //var el = this.getRegion(region).el.createChild();
32127 *var el = this.el.createChild();
32128 // create the grid first...
32129 cfg.grid.container = el;
32130 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
32133 if (region == 'center' && this.active ) {
32134 cfg.background = false;
32137 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32139 this.add(region, ret);
32141 if (cfg.background) {
32142 // render grid on panel activation (if panel background)
32143 ret.on('activate', function(gp) {
32144 if (!gp.grid.rendered) {
32145 // gp.grid.render(el);
32149 // cfg.grid.render(el);
32155 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
32156 // it was the old xcomponent building that caused this before.
32157 // espeically if border is the top element in the tree.
32167 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32169 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32170 this.add(region, ret);
32174 throw "Can not add '" + cfg.xtype + "' to Border";
32180 this.beginUpdate();
32184 Roo.each(xitems, function(i) {
32185 region = nb && i.region ? i.region : false;
32187 var add = ret.addxtype(i);
32190 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32191 if (!i.background) {
32192 abn[region] = nb[region] ;
32199 // make the last non-background panel active..
32200 //if (nb) { Roo.log(abn); }
32203 for(var r in abn) {
32204 region = this.getRegion(r);
32206 // tried using nb[r], but it does not work..
32208 region.showPanel(abn[r]);
32219 factory : function(cfg)
32222 var validRegions = Roo.bootstrap.layout.Border.regions;
32224 var target = cfg.region;
32227 var r = Roo.bootstrap.layout;
32231 return new r.North(cfg);
32233 return new r.South(cfg);
32235 return new r.East(cfg);
32237 return new r.West(cfg);
32239 return new r.Center(cfg);
32241 throw 'Layout region "'+target+'" not supported.';
32248 * Ext JS Library 1.1.1
32249 * Copyright(c) 2006-2007, Ext JS, LLC.
32251 * Originally Released Under LGPL - original licence link has changed is not relivant.
32254 * <script type="text/javascript">
32258 * @class Roo.bootstrap.layout.Basic
32259 * @extends Roo.util.Observable
32260 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32261 * and does not have a titlebar, tabs or any other features. All it does is size and position
32262 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32263 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32264 * @cfg {string} region the region that it inhabits..
32265 * @cfg {bool} skipConfig skip config?
32269 Roo.bootstrap.layout.Basic = function(config){
32271 this.mgr = config.mgr;
32273 this.position = config.region;
32275 var skipConfig = config.skipConfig;
32279 * @scope Roo.BasicLayoutRegion
32283 * @event beforeremove
32284 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32285 * @param {Roo.LayoutRegion} this
32286 * @param {Roo.ContentPanel} panel The panel
32287 * @param {Object} e The cancel event object
32289 "beforeremove" : true,
32291 * @event invalidated
32292 * Fires when the layout for this region is changed.
32293 * @param {Roo.LayoutRegion} this
32295 "invalidated" : true,
32297 * @event visibilitychange
32298 * Fires when this region is shown or hidden
32299 * @param {Roo.LayoutRegion} this
32300 * @param {Boolean} visibility true or false
32302 "visibilitychange" : true,
32304 * @event paneladded
32305 * Fires when a panel is added.
32306 * @param {Roo.LayoutRegion} this
32307 * @param {Roo.ContentPanel} panel The panel
32309 "paneladded" : true,
32311 * @event panelremoved
32312 * Fires when a panel is removed.
32313 * @param {Roo.LayoutRegion} this
32314 * @param {Roo.ContentPanel} panel The panel
32316 "panelremoved" : true,
32318 * @event beforecollapse
32319 * Fires when this region before collapse.
32320 * @param {Roo.LayoutRegion} this
32322 "beforecollapse" : true,
32325 * Fires when this region is collapsed.
32326 * @param {Roo.LayoutRegion} this
32328 "collapsed" : true,
32331 * Fires when this region is expanded.
32332 * @param {Roo.LayoutRegion} this
32337 * Fires when this region is slid into view.
32338 * @param {Roo.LayoutRegion} this
32340 "slideshow" : true,
32343 * Fires when this region slides out of view.
32344 * @param {Roo.LayoutRegion} this
32346 "slidehide" : true,
32348 * @event panelactivated
32349 * Fires when a panel is activated.
32350 * @param {Roo.LayoutRegion} this
32351 * @param {Roo.ContentPanel} panel The activated panel
32353 "panelactivated" : true,
32356 * Fires when the user resizes this region.
32357 * @param {Roo.LayoutRegion} this
32358 * @param {Number} newSize The new size (width for east/west, height for north/south)
32362 /** A collection of panels in this region. @type Roo.util.MixedCollection */
32363 this.panels = new Roo.util.MixedCollection();
32364 this.panels.getKey = this.getPanelId.createDelegate(this);
32366 this.activePanel = null;
32367 // ensure listeners are added...
32369 if (config.listeners || config.events) {
32370 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32371 listeners : config.listeners || {},
32372 events : config.events || {}
32376 if(skipConfig !== true){
32377 this.applyConfig(config);
32381 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32383 getPanelId : function(p){
32387 applyConfig : function(config){
32388 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32389 this.config = config;
32394 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
32395 * the width, for horizontal (north, south) the height.
32396 * @param {Number} newSize The new width or height
32398 resizeTo : function(newSize){
32399 var el = this.el ? this.el :
32400 (this.activePanel ? this.activePanel.getEl() : null);
32402 switch(this.position){
32405 el.setWidth(newSize);
32406 this.fireEvent("resized", this, newSize);
32410 el.setHeight(newSize);
32411 this.fireEvent("resized", this, newSize);
32417 getBox : function(){
32418 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32421 getMargins : function(){
32422 return this.margins;
32425 updateBox : function(box){
32427 var el = this.activePanel.getEl();
32428 el.dom.style.left = box.x + "px";
32429 el.dom.style.top = box.y + "px";
32430 this.activePanel.setSize(box.width, box.height);
32434 * Returns the container element for this region.
32435 * @return {Roo.Element}
32437 getEl : function(){
32438 return this.activePanel;
32442 * Returns true if this region is currently visible.
32443 * @return {Boolean}
32445 isVisible : function(){
32446 return this.activePanel ? true : false;
32449 setActivePanel : function(panel){
32450 panel = this.getPanel(panel);
32451 if(this.activePanel && this.activePanel != panel){
32452 this.activePanel.setActiveState(false);
32453 this.activePanel.getEl().setLeftTop(-10000,-10000);
32455 this.activePanel = panel;
32456 panel.setActiveState(true);
32458 panel.setSize(this.box.width, this.box.height);
32460 this.fireEvent("panelactivated", this, panel);
32461 this.fireEvent("invalidated");
32465 * Show the specified panel.
32466 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32467 * @return {Roo.ContentPanel} The shown panel or null
32469 showPanel : function(panel){
32470 panel = this.getPanel(panel);
32472 this.setActivePanel(panel);
32478 * Get the active panel for this region.
32479 * @return {Roo.ContentPanel} The active panel or null
32481 getActivePanel : function(){
32482 return this.activePanel;
32486 * Add the passed ContentPanel(s)
32487 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32488 * @return {Roo.ContentPanel} The panel added (if only one was added)
32490 add : function(panel){
32491 if(arguments.length > 1){
32492 for(var i = 0, len = arguments.length; i < len; i++) {
32493 this.add(arguments[i]);
32497 if(this.hasPanel(panel)){
32498 this.showPanel(panel);
32501 var el = panel.getEl();
32502 if(el.dom.parentNode != this.mgr.el.dom){
32503 this.mgr.el.dom.appendChild(el.dom);
32505 if(panel.setRegion){
32506 panel.setRegion(this);
32508 this.panels.add(panel);
32509 el.setStyle("position", "absolute");
32510 if(!panel.background){
32511 this.setActivePanel(panel);
32512 if(this.config.initialSize && this.panels.getCount()==1){
32513 this.resizeTo(this.config.initialSize);
32516 this.fireEvent("paneladded", this, panel);
32521 * Returns true if the panel is in this region.
32522 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32523 * @return {Boolean}
32525 hasPanel : function(panel){
32526 if(typeof panel == "object"){ // must be panel obj
32527 panel = panel.getId();
32529 return this.getPanel(panel) ? true : false;
32533 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32534 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32535 * @param {Boolean} preservePanel Overrides the config preservePanel option
32536 * @return {Roo.ContentPanel} The panel that was removed
32538 remove : function(panel, preservePanel){
32539 panel = this.getPanel(panel);
32544 this.fireEvent("beforeremove", this, panel, e);
32545 if(e.cancel === true){
32548 var panelId = panel.getId();
32549 this.panels.removeKey(panelId);
32554 * Returns the panel specified or null if it's not in this region.
32555 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32556 * @return {Roo.ContentPanel}
32558 getPanel : function(id){
32559 if(typeof id == "object"){ // must be panel obj
32562 return this.panels.get(id);
32566 * Returns this regions position (north/south/east/west/center).
32569 getPosition: function(){
32570 return this.position;
32574 * Ext JS Library 1.1.1
32575 * Copyright(c) 2006-2007, Ext JS, LLC.
32577 * Originally Released Under LGPL - original licence link has changed is not relivant.
32580 * <script type="text/javascript">
32584 * @class Roo.bootstrap.layout.Region
32585 * @extends Roo.bootstrap.layout.Basic
32586 * This class represents a region in a layout manager.
32588 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32589 * @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})
32590 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
32591 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
32592 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
32593 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
32594 * @cfg {String} title The title for the region (overrides panel titles)
32595 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
32596 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32597 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
32598 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32599 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
32600 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32601 * the space available, similar to FireFox 1.5 tabs (defaults to false)
32602 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
32603 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
32604 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
32606 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
32607 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
32608 * @cfg {Boolean} disableTabTips True to disable tab tooltips
32609 * @cfg {Number} width For East/West panels
32610 * @cfg {Number} height For North/South panels
32611 * @cfg {Boolean} split To show the splitter
32612 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
32614 * @cfg {string} cls Extra CSS classes to add to region
32616 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32617 * @cfg {string} region the region that it inhabits..
32620 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
32621 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
32623 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
32624 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
32625 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
32627 Roo.bootstrap.layout.Region = function(config)
32629 this.applyConfig(config);
32631 var mgr = config.mgr;
32632 var pos = config.region;
32633 config.skipConfig = true;
32634 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32637 this.onRender(mgr.el);
32640 this.visible = true;
32641 this.collapsed = false;
32642 this.unrendered_panels = [];
32645 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32647 position: '', // set by wrapper (eg. north/south etc..)
32648 unrendered_panels : null, // unrendered panels.
32649 createBody : function(){
32650 /** This region's body element
32651 * @type Roo.Element */
32652 this.bodyEl = this.el.createChild({
32654 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32658 onRender: function(ctr, pos)
32660 var dh = Roo.DomHelper;
32661 /** This region's container element
32662 * @type Roo.Element */
32663 this.el = dh.append(ctr.dom, {
32665 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32667 /** This region's title element
32668 * @type Roo.Element */
32670 this.titleEl = dh.append(this.el.dom,
32673 unselectable: "on",
32674 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32676 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
32677 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32680 this.titleEl.enableDisplayMode();
32681 /** This region's title text element
32682 * @type HTMLElement */
32683 this.titleTextEl = this.titleEl.dom.firstChild;
32684 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32686 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32687 this.closeBtn.enableDisplayMode();
32688 this.closeBtn.on("click", this.closeClicked, this);
32689 this.closeBtn.hide();
32691 this.createBody(this.config);
32692 if(this.config.hideWhenEmpty){
32694 this.on("paneladded", this.validateVisibility, this);
32695 this.on("panelremoved", this.validateVisibility, this);
32697 if(this.autoScroll){
32698 this.bodyEl.setStyle("overflow", "auto");
32700 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32702 //if(c.titlebar !== false){
32703 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32704 this.titleEl.hide();
32706 this.titleEl.show();
32707 if(this.config.title){
32708 this.titleTextEl.innerHTML = this.config.title;
32712 if(this.config.collapsed){
32713 this.collapse(true);
32715 if(this.config.hidden){
32719 if (this.unrendered_panels && this.unrendered_panels.length) {
32720 for (var i =0;i< this.unrendered_panels.length; i++) {
32721 this.add(this.unrendered_panels[i]);
32723 this.unrendered_panels = null;
32729 applyConfig : function(c)
32732 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32733 var dh = Roo.DomHelper;
32734 if(c.titlebar !== false){
32735 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32736 this.collapseBtn.on("click", this.collapse, this);
32737 this.collapseBtn.enableDisplayMode();
32739 if(c.showPin === true || this.showPin){
32740 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32741 this.stickBtn.enableDisplayMode();
32742 this.stickBtn.on("click", this.expand, this);
32743 this.stickBtn.hide();
32748 /** This region's collapsed element
32749 * @type Roo.Element */
32752 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32753 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32756 if(c.floatable !== false){
32757 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32758 this.collapsedEl.on("click", this.collapseClick, this);
32761 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32762 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32763 id: "message", unselectable: "on", style:{"float":"left"}});
32764 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32766 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32767 this.expandBtn.on("click", this.expand, this);
32771 if(this.collapseBtn){
32772 this.collapseBtn.setVisible(c.collapsible == true);
32775 this.cmargins = c.cmargins || this.cmargins ||
32776 (this.position == "west" || this.position == "east" ?
32777 {top: 0, left: 2, right:2, bottom: 0} :
32778 {top: 2, left: 0, right:0, bottom: 2});
32780 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32783 this.bottomTabs = c.tabPosition != "top";
32785 this.autoScroll = c.autoScroll || false;
32790 this.duration = c.duration || .30;
32791 this.slideDuration = c.slideDuration || .45;
32796 * Returns true if this region is currently visible.
32797 * @return {Boolean}
32799 isVisible : function(){
32800 return this.visible;
32804 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32805 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
32807 //setCollapsedTitle : function(title){
32808 // title = title || " ";
32809 // if(this.collapsedTitleTextEl){
32810 // this.collapsedTitleTextEl.innerHTML = title;
32814 getBox : function(){
32816 // if(!this.collapsed){
32817 b = this.el.getBox(false, true);
32819 // b = this.collapsedEl.getBox(false, true);
32824 getMargins : function(){
32825 return this.margins;
32826 //return this.collapsed ? this.cmargins : this.margins;
32829 highlight : function(){
32830 this.el.addClass("x-layout-panel-dragover");
32833 unhighlight : function(){
32834 this.el.removeClass("x-layout-panel-dragover");
32837 updateBox : function(box)
32839 if (!this.bodyEl) {
32840 return; // not rendered yet..
32844 if(!this.collapsed){
32845 this.el.dom.style.left = box.x + "px";
32846 this.el.dom.style.top = box.y + "px";
32847 this.updateBody(box.width, box.height);
32849 this.collapsedEl.dom.style.left = box.x + "px";
32850 this.collapsedEl.dom.style.top = box.y + "px";
32851 this.collapsedEl.setSize(box.width, box.height);
32854 this.tabs.autoSizeTabs();
32858 updateBody : function(w, h)
32861 this.el.setWidth(w);
32862 w -= this.el.getBorderWidth("rl");
32863 if(this.config.adjustments){
32864 w += this.config.adjustments[0];
32868 this.el.setHeight(h);
32869 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32870 h -= this.el.getBorderWidth("tb");
32871 if(this.config.adjustments){
32872 h += this.config.adjustments[1];
32874 this.bodyEl.setHeight(h);
32876 h = this.tabs.syncHeight(h);
32879 if(this.panelSize){
32880 w = w !== null ? w : this.panelSize.width;
32881 h = h !== null ? h : this.panelSize.height;
32883 if(this.activePanel){
32884 var el = this.activePanel.getEl();
32885 w = w !== null ? w : el.getWidth();
32886 h = h !== null ? h : el.getHeight();
32887 this.panelSize = {width: w, height: h};
32888 this.activePanel.setSize(w, h);
32890 if(Roo.isIE && this.tabs){
32891 this.tabs.el.repaint();
32896 * Returns the container element for this region.
32897 * @return {Roo.Element}
32899 getEl : function(){
32904 * Hides this region.
32907 //if(!this.collapsed){
32908 this.el.dom.style.left = "-2000px";
32911 // this.collapsedEl.dom.style.left = "-2000px";
32912 // this.collapsedEl.hide();
32914 this.visible = false;
32915 this.fireEvent("visibilitychange", this, false);
32919 * Shows this region if it was previously hidden.
32922 //if(!this.collapsed){
32925 // this.collapsedEl.show();
32927 this.visible = true;
32928 this.fireEvent("visibilitychange", this, true);
32931 closeClicked : function(){
32932 if(this.activePanel){
32933 this.remove(this.activePanel);
32937 collapseClick : function(e){
32939 e.stopPropagation();
32942 e.stopPropagation();
32948 * Collapses this region.
32949 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32952 collapse : function(skipAnim, skipCheck = false){
32953 if(this.collapsed) {
32957 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32959 this.collapsed = true;
32961 this.split.el.hide();
32963 if(this.config.animate && skipAnim !== true){
32964 this.fireEvent("invalidated", this);
32965 this.animateCollapse();
32967 this.el.setLocation(-20000,-20000);
32969 this.collapsedEl.show();
32970 this.fireEvent("collapsed", this);
32971 this.fireEvent("invalidated", this);
32977 animateCollapse : function(){
32982 * Expands this region if it was previously collapsed.
32983 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32984 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32987 expand : function(e, skipAnim){
32989 e.stopPropagation();
32991 if(!this.collapsed || this.el.hasActiveFx()) {
32995 this.afterSlideIn();
32998 this.collapsed = false;
32999 if(this.config.animate && skipAnim !== true){
33000 this.animateExpand();
33004 this.split.el.show();
33006 this.collapsedEl.setLocation(-2000,-2000);
33007 this.collapsedEl.hide();
33008 this.fireEvent("invalidated", this);
33009 this.fireEvent("expanded", this);
33013 animateExpand : function(){
33017 initTabs : function()
33019 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33021 var ts = new Roo.bootstrap.panel.Tabs({
33022 el: this.bodyEl.dom,
33023 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33024 disableTooltips: this.config.disableTabTips,
33025 toolbar : this.config.toolbar
33028 if(this.config.hideTabs){
33029 ts.stripWrap.setDisplayed(false);
33032 ts.resizeTabs = this.config.resizeTabs === true;
33033 ts.minTabWidth = this.config.minTabWidth || 40;
33034 ts.maxTabWidth = this.config.maxTabWidth || 250;
33035 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33036 ts.monitorResize = false;
33037 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33038 ts.bodyEl.addClass('roo-layout-tabs-body');
33039 this.panels.each(this.initPanelAsTab, this);
33042 initPanelAsTab : function(panel){
33043 var ti = this.tabs.addTab(
33047 this.config.closeOnTab && panel.isClosable()
33049 if(panel.tabTip !== undefined){
33050 ti.setTooltip(panel.tabTip);
33052 ti.on("activate", function(){
33053 this.setActivePanel(panel);
33056 if(this.config.closeOnTab){
33057 ti.on("beforeclose", function(t, e){
33059 this.remove(panel);
33065 updatePanelTitle : function(panel, title)
33067 if(this.activePanel == panel){
33068 this.updateTitle(title);
33071 var ti = this.tabs.getTab(panel.getEl().id);
33073 if(panel.tabTip !== undefined){
33074 ti.setTooltip(panel.tabTip);
33079 updateTitle : function(title){
33080 if(this.titleTextEl && !this.config.title){
33081 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
33085 setActivePanel : function(panel)
33087 panel = this.getPanel(panel);
33088 if(this.activePanel && this.activePanel != panel){
33089 this.activePanel.setActiveState(false);
33091 this.activePanel = panel;
33092 panel.setActiveState(true);
33093 if(this.panelSize){
33094 panel.setSize(this.panelSize.width, this.panelSize.height);
33097 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33099 this.updateTitle(panel.getTitle());
33101 this.fireEvent("invalidated", this);
33103 this.fireEvent("panelactivated", this, panel);
33107 * Shows the specified panel.
33108 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33109 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33111 showPanel : function(panel)
33113 panel = this.getPanel(panel);
33116 var tab = this.tabs.getTab(panel.getEl().id);
33117 if(tab.isHidden()){
33118 this.tabs.unhideTab(tab.id);
33122 this.setActivePanel(panel);
33129 * Get the active panel for this region.
33130 * @return {Roo.ContentPanel} The active panel or null
33132 getActivePanel : function(){
33133 return this.activePanel;
33136 validateVisibility : function(){
33137 if(this.panels.getCount() < 1){
33138 this.updateTitle(" ");
33139 this.closeBtn.hide();
33142 if(!this.isVisible()){
33149 * Adds the passed ContentPanel(s) to this region.
33150 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33151 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33153 add : function(panel)
33155 if(arguments.length > 1){
33156 for(var i = 0, len = arguments.length; i < len; i++) {
33157 this.add(arguments[i]);
33162 // if we have not been rendered yet, then we can not really do much of this..
33163 if (!this.bodyEl) {
33164 this.unrendered_panels.push(panel);
33171 if(this.hasPanel(panel)){
33172 this.showPanel(panel);
33175 panel.setRegion(this);
33176 this.panels.add(panel);
33177 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33178 // sinle panel - no tab...?? would it not be better to render it with the tabs,
33179 // and hide them... ???
33180 this.bodyEl.dom.appendChild(panel.getEl().dom);
33181 if(panel.background !== true){
33182 this.setActivePanel(panel);
33184 this.fireEvent("paneladded", this, panel);
33191 this.initPanelAsTab(panel);
33195 if(panel.background !== true){
33196 this.tabs.activate(panel.getEl().id);
33198 this.fireEvent("paneladded", this, panel);
33203 * Hides the tab for the specified panel.
33204 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33206 hidePanel : function(panel){
33207 if(this.tabs && (panel = this.getPanel(panel))){
33208 this.tabs.hideTab(panel.getEl().id);
33213 * Unhides the tab for a previously hidden panel.
33214 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33216 unhidePanel : function(panel){
33217 if(this.tabs && (panel = this.getPanel(panel))){
33218 this.tabs.unhideTab(panel.getEl().id);
33222 clearPanels : function(){
33223 while(this.panels.getCount() > 0){
33224 this.remove(this.panels.first());
33229 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33230 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33231 * @param {Boolean} preservePanel Overrides the config preservePanel option
33232 * @return {Roo.ContentPanel} The panel that was removed
33234 remove : function(panel, preservePanel)
33236 panel = this.getPanel(panel);
33241 this.fireEvent("beforeremove", this, panel, e);
33242 if(e.cancel === true){
33245 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33246 var panelId = panel.getId();
33247 this.panels.removeKey(panelId);
33249 document.body.appendChild(panel.getEl().dom);
33252 this.tabs.removeTab(panel.getEl().id);
33253 }else if (!preservePanel){
33254 this.bodyEl.dom.removeChild(panel.getEl().dom);
33256 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33257 var p = this.panels.first();
33258 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33259 tempEl.appendChild(p.getEl().dom);
33260 this.bodyEl.update("");
33261 this.bodyEl.dom.appendChild(p.getEl().dom);
33263 this.updateTitle(p.getTitle());
33265 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33266 this.setActivePanel(p);
33268 panel.setRegion(null);
33269 if(this.activePanel == panel){
33270 this.activePanel = null;
33272 if(this.config.autoDestroy !== false && preservePanel !== true){
33273 try{panel.destroy();}catch(e){}
33275 this.fireEvent("panelremoved", this, panel);
33280 * Returns the TabPanel component used by this region
33281 * @return {Roo.TabPanel}
33283 getTabs : function(){
33287 createTool : function(parentEl, className){
33288 var btn = Roo.DomHelper.append(parentEl, {
33290 cls: "x-layout-tools-button",
33293 cls: "roo-layout-tools-button-inner " + className,
33297 btn.addClassOnOver("roo-layout-tools-button-over");
33302 * Ext JS Library 1.1.1
33303 * Copyright(c) 2006-2007, Ext JS, LLC.
33305 * Originally Released Under LGPL - original licence link has changed is not relivant.
33308 * <script type="text/javascript">
33314 * @class Roo.SplitLayoutRegion
33315 * @extends Roo.LayoutRegion
33316 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33318 Roo.bootstrap.layout.Split = function(config){
33319 this.cursor = config.cursor;
33320 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33323 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33325 splitTip : "Drag to resize.",
33326 collapsibleSplitTip : "Drag to resize. Double click to hide.",
33327 useSplitTips : false,
33329 applyConfig : function(config){
33330 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33333 onRender : function(ctr,pos) {
33335 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33336 if(!this.config.split){
33341 var splitEl = Roo.DomHelper.append(ctr.dom, {
33343 id: this.el.id + "-split",
33344 cls: "roo-layout-split roo-layout-split-"+this.position,
33347 /** The SplitBar for this region
33348 * @type Roo.SplitBar */
33349 // does not exist yet...
33350 Roo.log([this.position, this.orientation]);
33352 this.split = new Roo.bootstrap.SplitBar({
33353 dragElement : splitEl,
33354 resizingElement: this.el,
33355 orientation : this.orientation
33358 this.split.on("moved", this.onSplitMove, this);
33359 this.split.useShim = this.config.useShim === true;
33360 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33361 if(this.useSplitTips){
33362 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33364 //if(config.collapsible){
33365 // this.split.el.on("dblclick", this.collapse, this);
33368 if(typeof this.config.minSize != "undefined"){
33369 this.split.minSize = this.config.minSize;
33371 if(typeof this.config.maxSize != "undefined"){
33372 this.split.maxSize = this.config.maxSize;
33374 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33375 this.hideSplitter();
33380 getHMaxSize : function(){
33381 var cmax = this.config.maxSize || 10000;
33382 var center = this.mgr.getRegion("center");
33383 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33386 getVMaxSize : function(){
33387 var cmax = this.config.maxSize || 10000;
33388 var center = this.mgr.getRegion("center");
33389 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33392 onSplitMove : function(split, newSize){
33393 this.fireEvent("resized", this, newSize);
33397 * Returns the {@link Roo.SplitBar} for this region.
33398 * @return {Roo.SplitBar}
33400 getSplitBar : function(){
33405 this.hideSplitter();
33406 Roo.bootstrap.layout.Split.superclass.hide.call(this);
33409 hideSplitter : function(){
33411 this.split.el.setLocation(-2000,-2000);
33412 this.split.el.hide();
33418 this.split.el.show();
33420 Roo.bootstrap.layout.Split.superclass.show.call(this);
33423 beforeSlide: function(){
33424 if(Roo.isGecko){// firefox overflow auto bug workaround
33425 this.bodyEl.clip();
33427 this.tabs.bodyEl.clip();
33429 if(this.activePanel){
33430 this.activePanel.getEl().clip();
33432 if(this.activePanel.beforeSlide){
33433 this.activePanel.beforeSlide();
33439 afterSlide : function(){
33440 if(Roo.isGecko){// firefox overflow auto bug workaround
33441 this.bodyEl.unclip();
33443 this.tabs.bodyEl.unclip();
33445 if(this.activePanel){
33446 this.activePanel.getEl().unclip();
33447 if(this.activePanel.afterSlide){
33448 this.activePanel.afterSlide();
33454 initAutoHide : function(){
33455 if(this.autoHide !== false){
33456 if(!this.autoHideHd){
33457 var st = new Roo.util.DelayedTask(this.slideIn, this);
33458 this.autoHideHd = {
33459 "mouseout": function(e){
33460 if(!e.within(this.el, true)){
33464 "mouseover" : function(e){
33470 this.el.on(this.autoHideHd);
33474 clearAutoHide : function(){
33475 if(this.autoHide !== false){
33476 this.el.un("mouseout", this.autoHideHd.mouseout);
33477 this.el.un("mouseover", this.autoHideHd.mouseover);
33481 clearMonitor : function(){
33482 Roo.get(document).un("click", this.slideInIf, this);
33485 // these names are backwards but not changed for compat
33486 slideOut : function(){
33487 if(this.isSlid || this.el.hasActiveFx()){
33490 this.isSlid = true;
33491 if(this.collapseBtn){
33492 this.collapseBtn.hide();
33494 this.closeBtnState = this.closeBtn.getStyle('display');
33495 this.closeBtn.hide();
33497 this.stickBtn.show();
33500 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33501 this.beforeSlide();
33502 this.el.setStyle("z-index", 10001);
33503 this.el.slideIn(this.getSlideAnchor(), {
33504 callback: function(){
33506 this.initAutoHide();
33507 Roo.get(document).on("click", this.slideInIf, this);
33508 this.fireEvent("slideshow", this);
33515 afterSlideIn : function(){
33516 this.clearAutoHide();
33517 this.isSlid = false;
33518 this.clearMonitor();
33519 this.el.setStyle("z-index", "");
33520 if(this.collapseBtn){
33521 this.collapseBtn.show();
33523 this.closeBtn.setStyle('display', this.closeBtnState);
33525 this.stickBtn.hide();
33527 this.fireEvent("slidehide", this);
33530 slideIn : function(cb){
33531 if(!this.isSlid || this.el.hasActiveFx()){
33535 this.isSlid = false;
33536 this.beforeSlide();
33537 this.el.slideOut(this.getSlideAnchor(), {
33538 callback: function(){
33539 this.el.setLeftTop(-10000, -10000);
33541 this.afterSlideIn();
33549 slideInIf : function(e){
33550 if(!e.within(this.el)){
33555 animateCollapse : function(){
33556 this.beforeSlide();
33557 this.el.setStyle("z-index", 20000);
33558 var anchor = this.getSlideAnchor();
33559 this.el.slideOut(anchor, {
33560 callback : function(){
33561 this.el.setStyle("z-index", "");
33562 this.collapsedEl.slideIn(anchor, {duration:.3});
33564 this.el.setLocation(-10000,-10000);
33566 this.fireEvent("collapsed", this);
33573 animateExpand : function(){
33574 this.beforeSlide();
33575 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33576 this.el.setStyle("z-index", 20000);
33577 this.collapsedEl.hide({
33580 this.el.slideIn(this.getSlideAnchor(), {
33581 callback : function(){
33582 this.el.setStyle("z-index", "");
33585 this.split.el.show();
33587 this.fireEvent("invalidated", this);
33588 this.fireEvent("expanded", this);
33616 getAnchor : function(){
33617 return this.anchors[this.position];
33620 getCollapseAnchor : function(){
33621 return this.canchors[this.position];
33624 getSlideAnchor : function(){
33625 return this.sanchors[this.position];
33628 getAlignAdj : function(){
33629 var cm = this.cmargins;
33630 switch(this.position){
33646 getExpandAdj : function(){
33647 var c = this.collapsedEl, cm = this.cmargins;
33648 switch(this.position){
33650 return [-(cm.right+c.getWidth()+cm.left), 0];
33653 return [cm.right+c.getWidth()+cm.left, 0];
33656 return [0, -(cm.top+cm.bottom+c.getHeight())];
33659 return [0, cm.top+cm.bottom+c.getHeight()];
33665 * Ext JS Library 1.1.1
33666 * Copyright(c) 2006-2007, Ext JS, LLC.
33668 * Originally Released Under LGPL - original licence link has changed is not relivant.
33671 * <script type="text/javascript">
33674 * These classes are private internal classes
33676 Roo.bootstrap.layout.Center = function(config){
33677 config.region = "center";
33678 Roo.bootstrap.layout.Region.call(this, config);
33679 this.visible = true;
33680 this.minWidth = config.minWidth || 20;
33681 this.minHeight = config.minHeight || 20;
33684 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33686 // center panel can't be hidden
33690 // center panel can't be hidden
33693 getMinWidth: function(){
33694 return this.minWidth;
33697 getMinHeight: function(){
33698 return this.minHeight;
33711 Roo.bootstrap.layout.North = function(config)
33713 config.region = 'north';
33714 config.cursor = 'n-resize';
33716 Roo.bootstrap.layout.Split.call(this, config);
33720 this.split.placement = Roo.bootstrap.SplitBar.TOP;
33721 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33722 this.split.el.addClass("roo-layout-split-v");
33724 var size = config.initialSize || config.height;
33725 if(typeof size != "undefined"){
33726 this.el.setHeight(size);
33729 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33731 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33735 getBox : function(){
33736 if(this.collapsed){
33737 return this.collapsedEl.getBox();
33739 var box = this.el.getBox();
33741 box.height += this.split.el.getHeight();
33746 updateBox : function(box){
33747 if(this.split && !this.collapsed){
33748 box.height -= this.split.el.getHeight();
33749 this.split.el.setLeft(box.x);
33750 this.split.el.setTop(box.y+box.height);
33751 this.split.el.setWidth(box.width);
33753 if(this.collapsed){
33754 this.updateBody(box.width, null);
33756 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33764 Roo.bootstrap.layout.South = function(config){
33765 config.region = 'south';
33766 config.cursor = 's-resize';
33767 Roo.bootstrap.layout.Split.call(this, config);
33769 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33770 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33771 this.split.el.addClass("roo-layout-split-v");
33773 var size = config.initialSize || config.height;
33774 if(typeof size != "undefined"){
33775 this.el.setHeight(size);
33779 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33780 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33781 getBox : function(){
33782 if(this.collapsed){
33783 return this.collapsedEl.getBox();
33785 var box = this.el.getBox();
33787 var sh = this.split.el.getHeight();
33794 updateBox : function(box){
33795 if(this.split && !this.collapsed){
33796 var sh = this.split.el.getHeight();
33799 this.split.el.setLeft(box.x);
33800 this.split.el.setTop(box.y-sh);
33801 this.split.el.setWidth(box.width);
33803 if(this.collapsed){
33804 this.updateBody(box.width, null);
33806 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33810 Roo.bootstrap.layout.East = function(config){
33811 config.region = "east";
33812 config.cursor = "e-resize";
33813 Roo.bootstrap.layout.Split.call(this, config);
33815 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33816 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33817 this.split.el.addClass("roo-layout-split-h");
33819 var size = config.initialSize || config.width;
33820 if(typeof size != "undefined"){
33821 this.el.setWidth(size);
33824 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33825 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33826 getBox : function(){
33827 if(this.collapsed){
33828 return this.collapsedEl.getBox();
33830 var box = this.el.getBox();
33832 var sw = this.split.el.getWidth();
33839 updateBox : function(box){
33840 if(this.split && !this.collapsed){
33841 var sw = this.split.el.getWidth();
33843 this.split.el.setLeft(box.x);
33844 this.split.el.setTop(box.y);
33845 this.split.el.setHeight(box.height);
33848 if(this.collapsed){
33849 this.updateBody(null, box.height);
33851 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33855 Roo.bootstrap.layout.West = function(config){
33856 config.region = "west";
33857 config.cursor = "w-resize";
33859 Roo.bootstrap.layout.Split.call(this, config);
33861 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33862 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33863 this.split.el.addClass("roo-layout-split-h");
33867 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33868 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33870 onRender: function(ctr, pos)
33872 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33873 var size = this.config.initialSize || this.config.width;
33874 if(typeof size != "undefined"){
33875 this.el.setWidth(size);
33879 getBox : function(){
33880 if(this.collapsed){
33881 return this.collapsedEl.getBox();
33883 var box = this.el.getBox();
33885 box.width += this.split.el.getWidth();
33890 updateBox : function(box){
33891 if(this.split && !this.collapsed){
33892 var sw = this.split.el.getWidth();
33894 this.split.el.setLeft(box.x+box.width);
33895 this.split.el.setTop(box.y);
33896 this.split.el.setHeight(box.height);
33898 if(this.collapsed){
33899 this.updateBody(null, box.height);
33901 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33904 Roo.namespace("Roo.bootstrap.panel");/*
33906 * Ext JS Library 1.1.1
33907 * Copyright(c) 2006-2007, Ext JS, LLC.
33909 * Originally Released Under LGPL - original licence link has changed is not relivant.
33912 * <script type="text/javascript">
33915 * @class Roo.ContentPanel
33916 * @extends Roo.util.Observable
33917 * A basic ContentPanel element.
33918 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
33919 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
33920 * @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
33921 * @cfg {Boolean} closable True if the panel can be closed/removed
33922 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
33923 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33924 * @cfg {Toolbar} toolbar A toolbar for this panel
33925 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
33926 * @cfg {String} title The title for this panel
33927 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33928 * @cfg {String} url Calls {@link #setUrl} with this value
33929 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33930 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
33931 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
33932 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
33935 * Create a new ContentPanel.
33936 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33937 * @param {String/Object} config A string to set only the title or a config object
33938 * @param {String} content (optional) Set the HTML content for this panel
33939 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33941 Roo.bootstrap.panel.Content = function( config){
33943 var el = config.el;
33944 var content = config.content;
33946 if(config.autoCreate){ // xtype is available if this is called from factory
33949 this.el = Roo.get(el);
33950 if(!this.el && config && config.autoCreate){
33951 if(typeof config.autoCreate == "object"){
33952 if(!config.autoCreate.id){
33953 config.autoCreate.id = config.id||el;
33955 this.el = Roo.DomHelper.append(document.body,
33956 config.autoCreate, true);
33958 var elcfg = { tag: "div",
33959 cls: "roo-layout-inactive-content",
33963 elcfg.html = config.html;
33967 this.el = Roo.DomHelper.append(document.body, elcfg , true);
33970 this.closable = false;
33971 this.loaded = false;
33972 this.active = false;
33975 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33977 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33979 this.wrapEl = this.el.wrap();
33981 if (config.toolbar.items) {
33982 ti = config.toolbar.items ;
33983 delete config.toolbar.items ;
33987 this.toolbar.render(this.wrapEl, 'before');
33988 for(var i =0;i < ti.length;i++) {
33989 // Roo.log(['add child', items[i]]);
33990 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33992 this.toolbar.items = nitems;
33993 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33994 delete config.toolbar;
33998 // xtype created footer. - not sure if will work as we normally have to render first..
33999 if (this.footer && !this.footer.el && this.footer.xtype) {
34000 if (!this.wrapEl) {
34001 this.wrapEl = this.el.wrap();
34004 this.footer.container = this.wrapEl.createChild();
34006 this.footer = Roo.factory(this.footer, Roo);
34011 if(typeof config == "string"){
34012 this.title = config;
34014 Roo.apply(this, config);
34018 this.resizeEl = Roo.get(this.resizeEl, true);
34020 this.resizeEl = this.el;
34022 // handle view.xtype
34030 * Fires when this panel is activated.
34031 * @param {Roo.ContentPanel} this
34035 * @event deactivate
34036 * Fires when this panel is activated.
34037 * @param {Roo.ContentPanel} this
34039 "deactivate" : true,
34043 * Fires when this panel is resized if fitToFrame is true.
34044 * @param {Roo.ContentPanel} this
34045 * @param {Number} width The width after any component adjustments
34046 * @param {Number} height The height after any component adjustments
34052 * Fires when this tab is created
34053 * @param {Roo.ContentPanel} this
34064 if(this.autoScroll){
34065 this.resizeEl.setStyle("overflow", "auto");
34067 // fix randome scrolling
34068 //this.el.on('scroll', function() {
34069 // Roo.log('fix random scolling');
34070 // this.scrollTo('top',0);
34073 content = content || this.content;
34075 this.setContent(content);
34077 if(config && config.url){
34078 this.setUrl(this.url, this.params, this.loadOnce);
34083 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34085 if (this.view && typeof(this.view.xtype) != 'undefined') {
34086 this.view.el = this.el.appendChild(document.createElement("div"));
34087 this.view = Roo.factory(this.view);
34088 this.view.render && this.view.render(false, '');
34092 this.fireEvent('render', this);
34095 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34097 setRegion : function(region){
34098 this.region = region;
34100 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
34102 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
34107 * Returns the toolbar for this Panel if one was configured.
34108 * @return {Roo.Toolbar}
34110 getToolbar : function(){
34111 return this.toolbar;
34114 setActiveState : function(active){
34115 this.active = active;
34117 this.fireEvent("deactivate", this);
34119 this.fireEvent("activate", this);
34123 * Updates this panel's element
34124 * @param {String} content The new content
34125 * @param {Boolean} loadScripts (optional) true to look for and process scripts
34127 setContent : function(content, loadScripts){
34128 this.el.update(content, loadScripts);
34131 ignoreResize : function(w, h){
34132 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34135 this.lastSize = {width: w, height: h};
34140 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34141 * @return {Roo.UpdateManager} The UpdateManager
34143 getUpdateManager : function(){
34144 return this.el.getUpdateManager();
34147 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34148 * @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:
34151 url: "your-url.php",
34152 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34153 callback: yourFunction,
34154 scope: yourObject, //(optional scope)
34157 text: "Loading...",
34162 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34163 * 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.
34164 * @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}
34165 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34166 * @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.
34167 * @return {Roo.ContentPanel} this
34170 var um = this.el.getUpdateManager();
34171 um.update.apply(um, arguments);
34177 * 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.
34178 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34179 * @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)
34180 * @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)
34181 * @return {Roo.UpdateManager} The UpdateManager
34183 setUrl : function(url, params, loadOnce){
34184 if(this.refreshDelegate){
34185 this.removeListener("activate", this.refreshDelegate);
34187 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34188 this.on("activate", this.refreshDelegate);
34189 return this.el.getUpdateManager();
34192 _handleRefresh : function(url, params, loadOnce){
34193 if(!loadOnce || !this.loaded){
34194 var updater = this.el.getUpdateManager();
34195 updater.update(url, params, this._setLoaded.createDelegate(this));
34199 _setLoaded : function(){
34200 this.loaded = true;
34204 * Returns this panel's id
34207 getId : function(){
34212 * Returns this panel's element - used by regiosn to add.
34213 * @return {Roo.Element}
34215 getEl : function(){
34216 return this.wrapEl || this.el;
34221 adjustForComponents : function(width, height)
34223 //Roo.log('adjustForComponents ');
34224 if(this.resizeEl != this.el){
34225 width -= this.el.getFrameWidth('lr');
34226 height -= this.el.getFrameWidth('tb');
34229 var te = this.toolbar.getEl();
34230 height -= te.getHeight();
34231 te.setWidth(width);
34234 var te = this.footer.getEl();
34235 Roo.log("footer:" + te.getHeight());
34237 height -= te.getHeight();
34238 te.setWidth(width);
34242 if(this.adjustments){
34243 width += this.adjustments[0];
34244 height += this.adjustments[1];
34246 return {"width": width, "height": height};
34249 setSize : function(width, height){
34250 if(this.fitToFrame && !this.ignoreResize(width, height)){
34251 if(this.fitContainer && this.resizeEl != this.el){
34252 this.el.setSize(width, height);
34254 var size = this.adjustForComponents(width, height);
34255 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34256 this.fireEvent('resize', this, size.width, size.height);
34261 * Returns this panel's title
34264 getTitle : function(){
34269 * Set this panel's title
34270 * @param {String} title
34272 setTitle : function(title){
34273 this.title = title;
34275 this.region.updatePanelTitle(this, title);
34280 * Returns true is this panel was configured to be closable
34281 * @return {Boolean}
34283 isClosable : function(){
34284 return this.closable;
34287 beforeSlide : function(){
34289 this.resizeEl.clip();
34292 afterSlide : function(){
34294 this.resizeEl.unclip();
34298 * Force a content refresh from the URL specified in the {@link #setUrl} method.
34299 * Will fail silently if the {@link #setUrl} method has not been called.
34300 * This does not activate the panel, just updates its content.
34302 refresh : function(){
34303 if(this.refreshDelegate){
34304 this.loaded = false;
34305 this.refreshDelegate();
34310 * Destroys this panel
34312 destroy : function(){
34313 this.el.removeAllListeners();
34314 var tempEl = document.createElement("span");
34315 tempEl.appendChild(this.el.dom);
34316 tempEl.innerHTML = "";
34322 * form - if the content panel contains a form - this is a reference to it.
34323 * @type {Roo.form.Form}
34327 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34328 * This contains a reference to it.
34334 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34344 * @param {Object} cfg Xtype definition of item to add.
34348 getChildContainer: function () {
34349 return this.getEl();
34354 var ret = new Roo.factory(cfg);
34359 if (cfg.xtype.match(/^Form$/)) {
34362 //if (this.footer) {
34363 // el = this.footer.container.insertSibling(false, 'before');
34365 el = this.el.createChild();
34368 this.form = new Roo.form.Form(cfg);
34371 if ( this.form.allItems.length) {
34372 this.form.render(el.dom);
34376 // should only have one of theses..
34377 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34378 // views.. should not be just added - used named prop 'view''
34380 cfg.el = this.el.appendChild(document.createElement("div"));
34383 var ret = new Roo.factory(cfg);
34385 ret.render && ret.render(false, ''); // render blank..
34395 * @class Roo.bootstrap.panel.Grid
34396 * @extends Roo.bootstrap.panel.Content
34398 * Create a new GridPanel.
34399 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34400 * @param {Object} config A the config object
34406 Roo.bootstrap.panel.Grid = function(config)
34410 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34411 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34413 config.el = this.wrapper;
34414 //this.el = this.wrapper;
34416 if (config.container) {
34417 // ctor'ed from a Border/panel.grid
34420 this.wrapper.setStyle("overflow", "hidden");
34421 this.wrapper.addClass('roo-grid-container');
34426 if(config.toolbar){
34427 var tool_el = this.wrapper.createChild();
34428 this.toolbar = Roo.factory(config.toolbar);
34430 if (config.toolbar.items) {
34431 ti = config.toolbar.items ;
34432 delete config.toolbar.items ;
34436 this.toolbar.render(tool_el);
34437 for(var i =0;i < ti.length;i++) {
34438 // Roo.log(['add child', items[i]]);
34439 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34441 this.toolbar.items = nitems;
34443 delete config.toolbar;
34446 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34447 config.grid.scrollBody = true;;
34448 config.grid.monitorWindowResize = false; // turn off autosizing
34449 config.grid.autoHeight = false;
34450 config.grid.autoWidth = false;
34452 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34454 if (config.background) {
34455 // render grid on panel activation (if panel background)
34456 this.on('activate', function(gp) {
34457 if (!gp.grid.rendered) {
34458 gp.grid.render(el);
34459 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34465 this.grid.render(this.wrapper);
34466 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34469 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34470 // ??? needed ??? config.el = this.wrapper;
34475 // xtype created footer. - not sure if will work as we normally have to render first..
34476 if (this.footer && !this.footer.el && this.footer.xtype) {
34478 var ctr = this.grid.getView().getFooterPanel(true);
34479 this.footer.dataSource = this.grid.dataSource;
34480 this.footer = Roo.factory(this.footer, Roo);
34481 this.footer.render(ctr);
34491 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34492 getId : function(){
34493 return this.grid.id;
34497 * Returns the grid for this panel
34498 * @return {Roo.bootstrap.Table}
34500 getGrid : function(){
34504 setSize : function(width, height){
34505 if(!this.ignoreResize(width, height)){
34506 var grid = this.grid;
34507 var size = this.adjustForComponents(width, height);
34508 var gridel = grid.getGridEl();
34509 gridel.setSize(size.width, size.height);
34511 var thd = grid.getGridEl().select('thead',true).first();
34512 var tbd = grid.getGridEl().select('tbody', true).first();
34514 tbd.setSize(width, height - thd.getHeight());
34523 beforeSlide : function(){
34524 this.grid.getView().scroller.clip();
34527 afterSlide : function(){
34528 this.grid.getView().scroller.unclip();
34531 destroy : function(){
34532 this.grid.destroy();
34534 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
34539 * @class Roo.bootstrap.panel.Nest
34540 * @extends Roo.bootstrap.panel.Content
34542 * Create a new Panel, that can contain a layout.Border.
34545 * @param {Roo.BorderLayout} layout The layout for this panel
34546 * @param {String/Object} config A string to set only the title or a config object
34548 Roo.bootstrap.panel.Nest = function(config)
34550 // construct with only one argument..
34551 /* FIXME - implement nicer consturctors
34552 if (layout.layout) {
34554 layout = config.layout;
34555 delete config.layout;
34557 if (layout.xtype && !layout.getEl) {
34558 // then layout needs constructing..
34559 layout = Roo.factory(layout, Roo);
34563 config.el = config.layout.getEl();
34565 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34567 config.layout.monitorWindowResize = false; // turn off autosizing
34568 this.layout = config.layout;
34569 this.layout.getEl().addClass("roo-layout-nested-layout");
34576 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34578 setSize : function(width, height){
34579 if(!this.ignoreResize(width, height)){
34580 var size = this.adjustForComponents(width, height);
34581 var el = this.layout.getEl();
34582 el.setSize(size.width, size.height);
34583 var touch = el.dom.offsetWidth;
34584 this.layout.layout();
34585 // ie requires a double layout on the first pass
34586 if(Roo.isIE && !this.initialized){
34587 this.initialized = true;
34588 this.layout.layout();
34593 // activate all subpanels if not currently active..
34595 setActiveState : function(active){
34596 this.active = active;
34598 this.fireEvent("deactivate", this);
34602 this.fireEvent("activate", this);
34603 // not sure if this should happen before or after..
34604 if (!this.layout) {
34605 return; // should not happen..
34608 for (var r in this.layout.regions) {
34609 reg = this.layout.getRegion(r);
34610 if (reg.getActivePanel()) {
34611 //reg.showPanel(reg.getActivePanel()); // force it to activate..
34612 reg.setActivePanel(reg.getActivePanel());
34615 if (!reg.panels.length) {
34618 reg.showPanel(reg.getPanel(0));
34627 * Returns the nested BorderLayout for this panel
34628 * @return {Roo.BorderLayout}
34630 getLayout : function(){
34631 return this.layout;
34635 * Adds a xtype elements to the layout of the nested panel
34639 xtype : 'ContentPanel',
34646 xtype : 'NestedLayoutPanel',
34652 items : [ ... list of content panels or nested layout panels.. ]
34656 * @param {Object} cfg Xtype definition of item to add.
34658 addxtype : function(cfg) {
34659 return this.layout.addxtype(cfg);
34664 * Ext JS Library 1.1.1
34665 * Copyright(c) 2006-2007, Ext JS, LLC.
34667 * Originally Released Under LGPL - original licence link has changed is not relivant.
34670 * <script type="text/javascript">
34673 * @class Roo.TabPanel
34674 * @extends Roo.util.Observable
34675 * A lightweight tab container.
34679 // basic tabs 1, built from existing content
34680 var tabs = new Roo.TabPanel("tabs1");
34681 tabs.addTab("script", "View Script");
34682 tabs.addTab("markup", "View Markup");
34683 tabs.activate("script");
34685 // more advanced tabs, built from javascript
34686 var jtabs = new Roo.TabPanel("jtabs");
34687 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34689 // set up the UpdateManager
34690 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34691 var updater = tab2.getUpdateManager();
34692 updater.setDefaultUrl("ajax1.htm");
34693 tab2.on('activate', updater.refresh, updater, true);
34695 // Use setUrl for Ajax loading
34696 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34697 tab3.setUrl("ajax2.htm", null, true);
34700 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34703 jtabs.activate("jtabs-1");
34706 * Create a new TabPanel.
34707 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34708 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34710 Roo.bootstrap.panel.Tabs = function(config){
34712 * The container element for this TabPanel.
34713 * @type Roo.Element
34715 this.el = Roo.get(config.el);
34718 if(typeof config == "boolean"){
34719 this.tabPosition = config ? "bottom" : "top";
34721 Roo.apply(this, config);
34725 if(this.tabPosition == "bottom"){
34726 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34727 this.el.addClass("roo-tabs-bottom");
34729 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34730 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34731 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34733 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34735 if(this.tabPosition != "bottom"){
34736 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34737 * @type Roo.Element
34739 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34740 this.el.addClass("roo-tabs-top");
34744 this.bodyEl.setStyle("position", "relative");
34746 this.active = null;
34747 this.activateDelegate = this.activate.createDelegate(this);
34752 * Fires when the active tab changes
34753 * @param {Roo.TabPanel} this
34754 * @param {Roo.TabPanelItem} activePanel The new active tab
34758 * @event beforetabchange
34759 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34760 * @param {Roo.TabPanel} this
34761 * @param {Object} e Set cancel to true on this object to cancel the tab change
34762 * @param {Roo.TabPanelItem} tab The tab being changed to
34764 "beforetabchange" : true
34767 Roo.EventManager.onWindowResize(this.onResize, this);
34768 this.cpad = this.el.getPadding("lr");
34769 this.hiddenCount = 0;
34772 // toolbar on the tabbar support...
34773 if (this.toolbar) {
34774 alert("no toolbar support yet");
34775 this.toolbar = false;
34777 var tcfg = this.toolbar;
34778 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
34779 this.toolbar = new Roo.Toolbar(tcfg);
34780 if (Roo.isSafari) {
34781 var tbl = tcfg.container.child('table', true);
34782 tbl.setAttribute('width', '100%');
34790 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34793 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34795 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34797 tabPosition : "top",
34799 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34801 currentTabWidth : 0,
34803 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34807 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34811 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34813 preferredTabWidth : 175,
34815 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34817 resizeTabs : false,
34819 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34821 monitorResize : true,
34823 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
34828 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34829 * @param {String} id The id of the div to use <b>or create</b>
34830 * @param {String} text The text for the tab
34831 * @param {String} content (optional) Content to put in the TabPanelItem body
34832 * @param {Boolean} closable (optional) True to create a close icon on the tab
34833 * @return {Roo.TabPanelItem} The created TabPanelItem
34835 addTab : function(id, text, content, closable)
34837 var item = new Roo.bootstrap.panel.TabItem({
34841 closable : closable
34843 this.addTabItem(item);
34845 item.setContent(content);
34851 * Returns the {@link Roo.TabPanelItem} with the specified id/index
34852 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34853 * @return {Roo.TabPanelItem}
34855 getTab : function(id){
34856 return this.items[id];
34860 * Hides the {@link Roo.TabPanelItem} with the specified id/index
34861 * @param {String/Number} id The id or index of the TabPanelItem to hide.
34863 hideTab : function(id){
34864 var t = this.items[id];
34867 this.hiddenCount++;
34868 this.autoSizeTabs();
34873 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34874 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34876 unhideTab : function(id){
34877 var t = this.items[id];
34879 t.setHidden(false);
34880 this.hiddenCount--;
34881 this.autoSizeTabs();
34886 * Adds an existing {@link Roo.TabPanelItem}.
34887 * @param {Roo.TabPanelItem} item The TabPanelItem to add
34889 addTabItem : function(item){
34890 this.items[item.id] = item;
34891 this.items.push(item);
34892 // if(this.resizeTabs){
34893 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34894 // this.autoSizeTabs();
34896 // item.autoSize();
34901 * Removes a {@link Roo.TabPanelItem}.
34902 * @param {String/Number} id The id or index of the TabPanelItem to remove.
34904 removeTab : function(id){
34905 var items = this.items;
34906 var tab = items[id];
34907 if(!tab) { return; }
34908 var index = items.indexOf(tab);
34909 if(this.active == tab && items.length > 1){
34910 var newTab = this.getNextAvailable(index);
34915 this.stripEl.dom.removeChild(tab.pnode.dom);
34916 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34917 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34919 items.splice(index, 1);
34920 delete this.items[tab.id];
34921 tab.fireEvent("close", tab);
34922 tab.purgeListeners();
34923 this.autoSizeTabs();
34926 getNextAvailable : function(start){
34927 var items = this.items;
34929 // look for a next tab that will slide over to
34930 // replace the one being removed
34931 while(index < items.length){
34932 var item = items[++index];
34933 if(item && !item.isHidden()){
34937 // if one isn't found select the previous tab (on the left)
34940 var item = items[--index];
34941 if(item && !item.isHidden()){
34949 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34950 * @param {String/Number} id The id or index of the TabPanelItem to disable.
34952 disableTab : function(id){
34953 var tab = this.items[id];
34954 if(tab && this.active != tab){
34960 * Enables a {@link Roo.TabPanelItem} that is disabled.
34961 * @param {String/Number} id The id or index of the TabPanelItem to enable.
34963 enableTab : function(id){
34964 var tab = this.items[id];
34969 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34970 * @param {String/Number} id The id or index of the TabPanelItem to activate.
34971 * @return {Roo.TabPanelItem} The TabPanelItem.
34973 activate : function(id){
34974 var tab = this.items[id];
34978 if(tab == this.active || tab.disabled){
34982 this.fireEvent("beforetabchange", this, e, tab);
34983 if(e.cancel !== true && !tab.disabled){
34985 this.active.hide();
34987 this.active = this.items[id];
34988 this.active.show();
34989 this.fireEvent("tabchange", this, this.active);
34995 * Gets the active {@link Roo.TabPanelItem}.
34996 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34998 getActiveTab : function(){
34999 return this.active;
35003 * Updates the tab body element to fit the height of the container element
35004 * for overflow scrolling
35005 * @param {Number} targetHeight (optional) Override the starting height from the elements height
35007 syncHeight : function(targetHeight){
35008 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35009 var bm = this.bodyEl.getMargins();
35010 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35011 this.bodyEl.setHeight(newHeight);
35015 onResize : function(){
35016 if(this.monitorResize){
35017 this.autoSizeTabs();
35022 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35024 beginUpdate : function(){
35025 this.updating = true;
35029 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35031 endUpdate : function(){
35032 this.updating = false;
35033 this.autoSizeTabs();
35037 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35039 autoSizeTabs : function(){
35040 var count = this.items.length;
35041 var vcount = count - this.hiddenCount;
35042 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35045 var w = Math.max(this.el.getWidth() - this.cpad, 10);
35046 var availWidth = Math.floor(w / vcount);
35047 var b = this.stripBody;
35048 if(b.getWidth() > w){
35049 var tabs = this.items;
35050 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35051 if(availWidth < this.minTabWidth){
35052 /*if(!this.sleft){ // incomplete scrolling code
35053 this.createScrollButtons();
35056 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35059 if(this.currentTabWidth < this.preferredTabWidth){
35060 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35066 * Returns the number of tabs in this TabPanel.
35069 getCount : function(){
35070 return this.items.length;
35074 * Resizes all the tabs to the passed width
35075 * @param {Number} The new width
35077 setTabWidth : function(width){
35078 this.currentTabWidth = width;
35079 for(var i = 0, len = this.items.length; i < len; i++) {
35080 if(!this.items[i].isHidden()) {
35081 this.items[i].setWidth(width);
35087 * Destroys this TabPanel
35088 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
35090 destroy : function(removeEl){
35091 Roo.EventManager.removeResizeListener(this.onResize, this);
35092 for(var i = 0, len = this.items.length; i < len; i++){
35093 this.items[i].purgeListeners();
35095 if(removeEl === true){
35096 this.el.update("");
35101 createStrip : function(container)
35103 var strip = document.createElement("nav");
35104 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
35105 container.appendChild(strip);
35109 createStripList : function(strip)
35111 // div wrapper for retard IE
35112 // returns the "tr" element.
35113 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
35114 //'<div class="x-tabs-strip-wrap">'+
35115 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
35116 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
35117 return strip.firstChild; //.firstChild.firstChild.firstChild;
35119 createBody : function(container)
35121 var body = document.createElement("div");
35122 Roo.id(body, "tab-body");
35123 //Roo.fly(body).addClass("x-tabs-body");
35124 Roo.fly(body).addClass("tab-content");
35125 container.appendChild(body);
35128 createItemBody :function(bodyEl, id){
35129 var body = Roo.getDom(id);
35131 body = document.createElement("div");
35134 //Roo.fly(body).addClass("x-tabs-item-body");
35135 Roo.fly(body).addClass("tab-pane");
35136 bodyEl.insertBefore(body, bodyEl.firstChild);
35140 createStripElements : function(stripEl, text, closable)
35142 var td = document.createElement("li"); // was td..
35145 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
35148 stripEl.appendChild(td);
35150 td.className = "x-tabs-closable";
35151 if(!this.closeTpl){
35152 this.closeTpl = new Roo.Template(
35153 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35154 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
35155 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
35158 var el = this.closeTpl.overwrite(td, {"text": text});
35159 var close = el.getElementsByTagName("div")[0];
35160 var inner = el.getElementsByTagName("em")[0];
35161 return {"el": el, "close": close, "inner": inner};
35164 // not sure what this is..
35166 //this.tabTpl = new Roo.Template(
35167 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35168 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
35170 this.tabTpl = new Roo.Template(
35172 '<span unselectable="on"' +
35173 (this.disableTooltips ? '' : ' title="{text}"') +
35174 ' >{text}</span></span></a>'
35178 var el = this.tabTpl.overwrite(td, {"text": text});
35179 var inner = el.getElementsByTagName("span")[0];
35180 return {"el": el, "inner": inner};
35188 * @class Roo.TabPanelItem
35189 * @extends Roo.util.Observable
35190 * Represents an individual item (tab plus body) in a TabPanel.
35191 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35192 * @param {String} id The id of this TabPanelItem
35193 * @param {String} text The text for the tab of this TabPanelItem
35194 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35196 Roo.bootstrap.panel.TabItem = function(config){
35198 * The {@link Roo.TabPanel} this TabPanelItem belongs to
35199 * @type Roo.TabPanel
35201 this.tabPanel = config.panel;
35203 * The id for this TabPanelItem
35206 this.id = config.id;
35208 this.disabled = false;
35210 this.text = config.text;
35212 this.loaded = false;
35213 this.closable = config.closable;
35216 * The body element for this TabPanelItem.
35217 * @type Roo.Element
35219 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35220 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35221 this.bodyEl.setStyle("display", "block");
35222 this.bodyEl.setStyle("zoom", "1");
35223 //this.hideAction();
35225 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
35227 this.el = Roo.get(els.el);
35228 this.inner = Roo.get(els.inner, true);
35229 this.textEl = Roo.get(this.el.dom.firstChild, true);
35230 this.pnode = Roo.get(els.el.parentNode, true);
35231 this.el.on("mousedown", this.onTabMouseDown, this);
35232 this.el.on("click", this.onTabClick, this);
35234 if(config.closable){
35235 var c = Roo.get(els.close, true);
35236 c.dom.title = this.closeText;
35237 c.addClassOnOver("close-over");
35238 c.on("click", this.closeClick, this);
35244 * Fires when this tab becomes the active tab.
35245 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35246 * @param {Roo.TabPanelItem} this
35250 * @event beforeclose
35251 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35252 * @param {Roo.TabPanelItem} this
35253 * @param {Object} e Set cancel to true on this object to cancel the close.
35255 "beforeclose": true,
35258 * Fires when this tab is closed.
35259 * @param {Roo.TabPanelItem} this
35263 * @event deactivate
35264 * Fires when this tab is no longer the active tab.
35265 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35266 * @param {Roo.TabPanelItem} this
35268 "deactivate" : true
35270 this.hidden = false;
35272 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35275 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35277 purgeListeners : function(){
35278 Roo.util.Observable.prototype.purgeListeners.call(this);
35279 this.el.removeAllListeners();
35282 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35285 this.pnode.addClass("active");
35288 this.tabPanel.stripWrap.repaint();
35290 this.fireEvent("activate", this.tabPanel, this);
35294 * Returns true if this tab is the active tab.
35295 * @return {Boolean}
35297 isActive : function(){
35298 return this.tabPanel.getActiveTab() == this;
35302 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35305 this.pnode.removeClass("active");
35307 this.fireEvent("deactivate", this.tabPanel, this);
35310 hideAction : function(){
35311 this.bodyEl.hide();
35312 this.bodyEl.setStyle("position", "absolute");
35313 this.bodyEl.setLeft("-20000px");
35314 this.bodyEl.setTop("-20000px");
35317 showAction : function(){
35318 this.bodyEl.setStyle("position", "relative");
35319 this.bodyEl.setTop("");
35320 this.bodyEl.setLeft("");
35321 this.bodyEl.show();
35325 * Set the tooltip for the tab.
35326 * @param {String} tooltip The tab's tooltip
35328 setTooltip : function(text){
35329 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35330 this.textEl.dom.qtip = text;
35331 this.textEl.dom.removeAttribute('title');
35333 this.textEl.dom.title = text;
35337 onTabClick : function(e){
35338 e.preventDefault();
35339 this.tabPanel.activate(this.id);
35342 onTabMouseDown : function(e){
35343 e.preventDefault();
35344 this.tabPanel.activate(this.id);
35347 getWidth : function(){
35348 return this.inner.getWidth();
35351 setWidth : function(width){
35352 var iwidth = width - this.pnode.getPadding("lr");
35353 this.inner.setWidth(iwidth);
35354 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35355 this.pnode.setWidth(width);
35359 * Show or hide the tab
35360 * @param {Boolean} hidden True to hide or false to show.
35362 setHidden : function(hidden){
35363 this.hidden = hidden;
35364 this.pnode.setStyle("display", hidden ? "none" : "");
35368 * Returns true if this tab is "hidden"
35369 * @return {Boolean}
35371 isHidden : function(){
35372 return this.hidden;
35376 * Returns the text for this tab
35379 getText : function(){
35383 autoSize : function(){
35384 //this.el.beginMeasure();
35385 this.textEl.setWidth(1);
35387 * #2804 [new] Tabs in Roojs
35388 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35390 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35391 //this.el.endMeasure();
35395 * Sets the text for the tab (Note: this also sets the tooltip text)
35396 * @param {String} text The tab's text and tooltip
35398 setText : function(text){
35400 this.textEl.update(text);
35401 this.setTooltip(text);
35402 //if(!this.tabPanel.resizeTabs){
35403 // this.autoSize();
35407 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35409 activate : function(){
35410 this.tabPanel.activate(this.id);
35414 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35416 disable : function(){
35417 if(this.tabPanel.active != this){
35418 this.disabled = true;
35419 this.pnode.addClass("disabled");
35424 * Enables this TabPanelItem if it was previously disabled.
35426 enable : function(){
35427 this.disabled = false;
35428 this.pnode.removeClass("disabled");
35432 * Sets the content for this TabPanelItem.
35433 * @param {String} content The content
35434 * @param {Boolean} loadScripts true to look for and load scripts
35436 setContent : function(content, loadScripts){
35437 this.bodyEl.update(content, loadScripts);
35441 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35442 * @return {Roo.UpdateManager} The UpdateManager
35444 getUpdateManager : function(){
35445 return this.bodyEl.getUpdateManager();
35449 * Set a URL to be used to load the content for this TabPanelItem.
35450 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35451 * @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)
35452 * @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)
35453 * @return {Roo.UpdateManager} The UpdateManager
35455 setUrl : function(url, params, loadOnce){
35456 if(this.refreshDelegate){
35457 this.un('activate', this.refreshDelegate);
35459 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35460 this.on("activate", this.refreshDelegate);
35461 return this.bodyEl.getUpdateManager();
35465 _handleRefresh : function(url, params, loadOnce){
35466 if(!loadOnce || !this.loaded){
35467 var updater = this.bodyEl.getUpdateManager();
35468 updater.update(url, params, this._setLoaded.createDelegate(this));
35473 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
35474 * Will fail silently if the setUrl method has not been called.
35475 * This does not activate the panel, just updates its content.
35477 refresh : function(){
35478 if(this.refreshDelegate){
35479 this.loaded = false;
35480 this.refreshDelegate();
35485 _setLoaded : function(){
35486 this.loaded = true;
35490 closeClick : function(e){
35493 this.fireEvent("beforeclose", this, o);
35494 if(o.cancel !== true){
35495 this.tabPanel.removeTab(this.id);
35499 * The text displayed in the tooltip for the close icon.
35502 closeText : "Close this tab"