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);
6433 onRemove : function(ds, record, index, isUpdate){
6434 if(isUpdate !== true){
6435 this.fireEvent("beforerowremoved", this, index, record);
6437 var bt = this.mainBody.dom;
6439 var rows = this.el.select('tbody > tr', true).elements;
6441 if(typeof(rows[index]) != 'undefined'){
6442 bt.removeChild(rows[index].dom);
6445 // if(bt.rows[index]){
6446 // bt.removeChild(bt.rows[index]);
6449 if(isUpdate !== true){
6450 //this.stripeRows(index);
6451 //this.syncRowHeights(index, index);
6453 this.fireEvent("rowremoved", this, index, record);
6457 onAdd : function(ds, records, rowIndex)
6459 //Roo.log('on Add called');
6460 // - note this does not handle multiple adding very well..
6461 var bt = this.mainBody.dom;
6462 for (var i =0 ; i < records.length;i++) {
6463 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6464 //Roo.log(records[i]);
6465 //Roo.log(this.store.getAt(rowIndex+i));
6466 this.insertRow(this.store, rowIndex + i, false);
6473 refreshRow : function(record){
6474 var ds = this.store, index;
6475 if(typeof record == 'number'){
6477 record = ds.getAt(index);
6479 index = ds.indexOf(record);
6481 this.insertRow(ds, index, true);
6482 this.onRemove(ds, record, index+1, true);
6483 //this.syncRowHeights(index, index);
6485 this.fireEvent("rowupdated", this, index, record);
6488 insertRow : function(dm, rowIndex, isUpdate){
6491 this.fireEvent("beforerowsinserted", this, rowIndex);
6493 //var s = this.getScrollState();
6494 var row = this.renderRow(this.cm, this.store, rowIndex);
6495 // insert before rowIndex..
6496 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6500 if(row.cellObjects.length){
6501 Roo.each(row.cellObjects, function(r){
6502 _this.renderCellObject(r);
6507 this.fireEvent("rowsinserted", this, rowIndex);
6508 //this.syncRowHeights(firstRow, lastRow);
6509 //this.stripeRows(firstRow);
6516 getRowDom : function(rowIndex)
6518 var rows = this.el.select('tbody > tr', true).elements;
6520 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6523 // returns the object tree for a tr..
6526 renderRow : function(cm, ds, rowIndex)
6529 var d = ds.getAt(rowIndex);
6536 var cellObjects = [];
6538 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6539 var config = cm.config[i];
6541 var renderer = cm.getRenderer(i);
6545 if(typeof(renderer) !== 'undefined'){
6546 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6548 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6549 // and are rendered into the cells after the row is rendered - using the id for the element.
6551 if(typeof(value) === 'object'){
6561 rowIndex : rowIndex,
6566 this.fireEvent('rowclass', this, rowcfg);
6570 cls : rowcfg.rowClass,
6572 html: (typeof(value) === 'object') ? '' : value
6579 if(typeof(config.colspan) != 'undefined'){
6580 td.colspan = config.colspan;
6583 if(typeof(config.hidden) != 'undefined' && config.hidden){
6584 td.style += ' display:none;';
6587 if(typeof(config.align) != 'undefined' && config.align.length){
6588 td.style += ' text-align:' + config.align + ';';
6591 if(typeof(config.width) != 'undefined'){
6592 td.style += ' width:' + config.width + 'px;';
6595 if(typeof(config.cursor) != 'undefined'){
6596 td.style += ' cursor:' + config.cursor + ';';
6599 if(typeof(config.cls) != 'undefined'){
6600 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6603 ['xs','sm','md','lg'].map(function(size){
6605 if(typeof(config[size]) == 'undefined'){
6609 if (!config[size]) { // 0 = hidden
6610 td.cls += ' hidden-' + size;
6614 td.cls += ' col-' + size + '-' + config[size];
6622 row.cellObjects = cellObjects;
6630 onBeforeLoad : function()
6632 //Roo.log('ds onBeforeLoad');
6636 //if(this.loadMask){
6637 // this.maskEl.show();
6645 this.el.select('tbody', true).first().dom.innerHTML = '';
6648 * Show or hide a row.
6649 * @param {Number} rowIndex to show or hide
6650 * @param {Boolean} state hide
6652 setRowVisibility : function(rowIndex, state)
6654 var bt = this.mainBody.dom;
6656 var rows = this.el.select('tbody > tr', true).elements;
6658 if(typeof(rows[rowIndex]) == 'undefined'){
6661 rows[rowIndex].dom.style.display = state ? '' : 'none';
6665 getSelectionModel : function(){
6667 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6669 return this.selModel;
6672 * Render the Roo.bootstrap object from renderder
6674 renderCellObject : function(r)
6678 var t = r.cfg.render(r.container);
6681 Roo.each(r.cfg.cn, function(c){
6683 container: t.getChildContainer(),
6686 _this.renderCellObject(child);
6691 getRowIndex : function(row)
6695 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6706 * Returns the grid's underlying element = used by panel.Grid
6707 * @return {Element} The element
6709 getGridEl : function(){
6713 * Forces a resize - used by panel.Grid
6714 * @return {Element} The element
6716 autoSize : function()
6718 //var ctr = Roo.get(this.container.dom.parentElement);
6719 var ctr = Roo.get(this.el.dom);
6721 var thd = this.getGridEl().select('thead',true).first();
6722 var tbd = this.getGridEl().select('tbody', true).first();
6725 var cw = ctr.getWidth();
6729 tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6730 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6733 cw = Math.max(cw, this.totalWidth);
6734 this.getGridEl().select('tr',true).setWidth(cw);
6735 // resize 'expandable coloumn?
6737 return; // we doe not have a view in this design..
6740 onBodyScroll: function()
6743 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6744 this.mainHead.setStyle({
6745 'position' : 'relative',
6746 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6763 * @class Roo.bootstrap.TableCell
6764 * @extends Roo.bootstrap.Component
6765 * Bootstrap TableCell class
6766 * @cfg {String} html cell contain text
6767 * @cfg {String} cls cell class
6768 * @cfg {String} tag cell tag (td|th) default td
6769 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6770 * @cfg {String} align Aligns the content in a cell
6771 * @cfg {String} axis Categorizes cells
6772 * @cfg {String} bgcolor Specifies the background color of a cell
6773 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6774 * @cfg {Number} colspan Specifies the number of columns a cell should span
6775 * @cfg {String} headers Specifies one or more header cells a cell is related to
6776 * @cfg {Number} height Sets the height of a cell
6777 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6778 * @cfg {Number} rowspan Sets the number of rows a cell should span
6779 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6780 * @cfg {String} valign Vertical aligns the content in a cell
6781 * @cfg {Number} width Specifies the width of a cell
6784 * Create a new TableCell
6785 * @param {Object} config The config object
6788 Roo.bootstrap.TableCell = function(config){
6789 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6792 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6812 getAutoCreate : function(){
6813 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6833 cfg.align=this.align
6839 cfg.bgcolor=this.bgcolor
6842 cfg.charoff=this.charoff
6845 cfg.colspan=this.colspan
6848 cfg.headers=this.headers
6851 cfg.height=this.height
6854 cfg.nowrap=this.nowrap
6857 cfg.rowspan=this.rowspan
6860 cfg.scope=this.scope
6863 cfg.valign=this.valign
6866 cfg.width=this.width
6885 * @class Roo.bootstrap.TableRow
6886 * @extends Roo.bootstrap.Component
6887 * Bootstrap TableRow class
6888 * @cfg {String} cls row class
6889 * @cfg {String} align Aligns the content in a table row
6890 * @cfg {String} bgcolor Specifies a background color for a table row
6891 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6892 * @cfg {String} valign Vertical aligns the content in a table row
6895 * Create a new TableRow
6896 * @param {Object} config The config object
6899 Roo.bootstrap.TableRow = function(config){
6900 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6903 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6911 getAutoCreate : function(){
6912 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6922 cfg.align = this.align;
6925 cfg.bgcolor = this.bgcolor;
6928 cfg.charoff = this.charoff;
6931 cfg.valign = this.valign;
6949 * @class Roo.bootstrap.TableBody
6950 * @extends Roo.bootstrap.Component
6951 * Bootstrap TableBody class
6952 * @cfg {String} cls element class
6953 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6954 * @cfg {String} align Aligns the content inside the element
6955 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6956 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6959 * Create a new TableBody
6960 * @param {Object} config The config object
6963 Roo.bootstrap.TableBody = function(config){
6964 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6967 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6975 getAutoCreate : function(){
6976 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6990 cfg.align = this.align;
6993 cfg.charoff = this.charoff;
6996 cfg.valign = this.valign;
7003 // initEvents : function()
7010 // this.store = Roo.factory(this.store, Roo.data);
7011 // this.store.on('load', this.onLoad, this);
7013 // this.store.load();
7017 // onLoad: function ()
7019 // this.fireEvent('load', this);
7029 * Ext JS Library 1.1.1
7030 * Copyright(c) 2006-2007, Ext JS, LLC.
7032 * Originally Released Under LGPL - original licence link has changed is not relivant.
7035 * <script type="text/javascript">
7038 // as we use this in bootstrap.
7039 Roo.namespace('Roo.form');
7041 * @class Roo.form.Action
7042 * Internal Class used to handle form actions
7044 * @param {Roo.form.BasicForm} el The form element or its id
7045 * @param {Object} config Configuration options
7050 // define the action interface
7051 Roo.form.Action = function(form, options){
7053 this.options = options || {};
7056 * Client Validation Failed
7059 Roo.form.Action.CLIENT_INVALID = 'client';
7061 * Server Validation Failed
7064 Roo.form.Action.SERVER_INVALID = 'server';
7066 * Connect to Server Failed
7069 Roo.form.Action.CONNECT_FAILURE = 'connect';
7071 * Reading Data from Server Failed
7074 Roo.form.Action.LOAD_FAILURE = 'load';
7076 Roo.form.Action.prototype = {
7078 failureType : undefined,
7079 response : undefined,
7083 run : function(options){
7088 success : function(response){
7093 handleResponse : function(response){
7097 // default connection failure
7098 failure : function(response){
7100 this.response = response;
7101 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7102 this.form.afterAction(this, false);
7105 processResponse : function(response){
7106 this.response = response;
7107 if(!response.responseText){
7110 this.result = this.handleResponse(response);
7114 // utility functions used internally
7115 getUrl : function(appendParams){
7116 var url = this.options.url || this.form.url || this.form.el.dom.action;
7118 var p = this.getParams();
7120 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7126 getMethod : function(){
7127 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7130 getParams : function(){
7131 var bp = this.form.baseParams;
7132 var p = this.options.params;
7134 if(typeof p == "object"){
7135 p = Roo.urlEncode(Roo.applyIf(p, bp));
7136 }else if(typeof p == 'string' && bp){
7137 p += '&' + Roo.urlEncode(bp);
7140 p = Roo.urlEncode(bp);
7145 createCallback : function(){
7147 success: this.success,
7148 failure: this.failure,
7150 timeout: (this.form.timeout*1000),
7151 upload: this.form.fileUpload ? this.success : undefined
7156 Roo.form.Action.Submit = function(form, options){
7157 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7160 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7163 haveProgress : false,
7164 uploadComplete : false,
7166 // uploadProgress indicator.
7167 uploadProgress : function()
7169 if (!this.form.progressUrl) {
7173 if (!this.haveProgress) {
7174 Roo.MessageBox.progress("Uploading", "Uploading");
7176 if (this.uploadComplete) {
7177 Roo.MessageBox.hide();
7181 this.haveProgress = true;
7183 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7185 var c = new Roo.data.Connection();
7187 url : this.form.progressUrl,
7192 success : function(req){
7193 //console.log(data);
7197 rdata = Roo.decode(req.responseText)
7199 Roo.log("Invalid data from server..");
7203 if (!rdata || !rdata.success) {
7205 Roo.MessageBox.alert(Roo.encode(rdata));
7208 var data = rdata.data;
7210 if (this.uploadComplete) {
7211 Roo.MessageBox.hide();
7216 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7217 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7220 this.uploadProgress.defer(2000,this);
7223 failure: function(data) {
7224 Roo.log('progress url failed ');
7235 // run get Values on the form, so it syncs any secondary forms.
7236 this.form.getValues();
7238 var o = this.options;
7239 var method = this.getMethod();
7240 var isPost = method == 'POST';
7241 if(o.clientValidation === false || this.form.isValid()){
7243 if (this.form.progressUrl) {
7244 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7245 (new Date() * 1) + '' + Math.random());
7250 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7251 form:this.form.el.dom,
7252 url:this.getUrl(!isPost),
7254 params:isPost ? this.getParams() : null,
7255 isUpload: this.form.fileUpload
7258 this.uploadProgress();
7260 }else if (o.clientValidation !== false){ // client validation failed
7261 this.failureType = Roo.form.Action.CLIENT_INVALID;
7262 this.form.afterAction(this, false);
7266 success : function(response)
7268 this.uploadComplete= true;
7269 if (this.haveProgress) {
7270 Roo.MessageBox.hide();
7274 var result = this.processResponse(response);
7275 if(result === true || result.success){
7276 this.form.afterAction(this, true);
7280 this.form.markInvalid(result.errors);
7281 this.failureType = Roo.form.Action.SERVER_INVALID;
7283 this.form.afterAction(this, false);
7285 failure : function(response)
7287 this.uploadComplete= true;
7288 if (this.haveProgress) {
7289 Roo.MessageBox.hide();
7292 this.response = response;
7293 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7294 this.form.afterAction(this, false);
7297 handleResponse : function(response){
7298 if(this.form.errorReader){
7299 var rs = this.form.errorReader.read(response);
7302 for(var i = 0, len = rs.records.length; i < len; i++) {
7303 var r = rs.records[i];
7307 if(errors.length < 1){
7311 success : rs.success,
7317 ret = Roo.decode(response.responseText);
7321 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7331 Roo.form.Action.Load = function(form, options){
7332 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7333 this.reader = this.form.reader;
7336 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7341 Roo.Ajax.request(Roo.apply(
7342 this.createCallback(), {
7343 method:this.getMethod(),
7344 url:this.getUrl(false),
7345 params:this.getParams()
7349 success : function(response){
7351 var result = this.processResponse(response);
7352 if(result === true || !result.success || !result.data){
7353 this.failureType = Roo.form.Action.LOAD_FAILURE;
7354 this.form.afterAction(this, false);
7357 this.form.clearInvalid();
7358 this.form.setValues(result.data);
7359 this.form.afterAction(this, true);
7362 handleResponse : function(response){
7363 if(this.form.reader){
7364 var rs = this.form.reader.read(response);
7365 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7367 success : rs.success,
7371 return Roo.decode(response.responseText);
7375 Roo.form.Action.ACTION_TYPES = {
7376 'load' : Roo.form.Action.Load,
7377 'submit' : Roo.form.Action.Submit
7386 * @class Roo.bootstrap.Form
7387 * @extends Roo.bootstrap.Component
7388 * Bootstrap Form class
7389 * @cfg {String} method GET | POST (default POST)
7390 * @cfg {String} labelAlign top | left (default top)
7391 * @cfg {String} align left | right - for navbars
7392 * @cfg {Boolean} loadMask load mask when submit (default true)
7397 * @param {Object} config The config object
7401 Roo.bootstrap.Form = function(config){
7402 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7405 * @event clientvalidation
7406 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7407 * @param {Form} this
7408 * @param {Boolean} valid true if the form has passed client-side validation
7410 clientvalidation: true,
7412 * @event beforeaction
7413 * Fires before any action is performed. Return false to cancel the action.
7414 * @param {Form} this
7415 * @param {Action} action The action to be performed
7419 * @event actionfailed
7420 * Fires when an action fails.
7421 * @param {Form} this
7422 * @param {Action} action The action that failed
7424 actionfailed : true,
7426 * @event actioncomplete
7427 * Fires when an action is completed.
7428 * @param {Form} this
7429 * @param {Action} action The action that completed
7431 actioncomplete : true
7436 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7439 * @cfg {String} method
7440 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7445 * The URL to use for form actions if one isn't supplied in the action options.
7448 * @cfg {Boolean} fileUpload
7449 * Set to true if this form is a file upload.
7453 * @cfg {Object} baseParams
7454 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7458 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7462 * @cfg {Sting} align (left|right) for navbar forms
7467 activeAction : null,
7470 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7471 * element by passing it or its id or mask the form itself by passing in true.
7474 waitMsgTarget : false,
7478 getAutoCreate : function(){
7482 method : this.method || 'POST',
7483 id : this.id || Roo.id(),
7486 if (this.parent().xtype.match(/^Nav/)) {
7487 cfg.cls = 'navbar-form navbar-' + this.align;
7491 if (this.labelAlign == 'left' ) {
7492 cfg.cls += ' form-horizontal';
7498 initEvents : function()
7500 this.el.on('submit', this.onSubmit, this);
7501 // this was added as random key presses on the form where triggering form submit.
7502 this.el.on('keypress', function(e) {
7503 if (e.getCharCode() != 13) {
7506 // we might need to allow it for textareas.. and some other items.
7507 // check e.getTarget().
7509 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7513 Roo.log("keypress blocked");
7521 onSubmit : function(e){
7526 * Returns true if client-side validation on the form is successful.
7529 isValid : function(){
7530 var items = this.getItems();
7532 items.each(function(f){
7541 * Returns true if any fields in this form have changed since their original load.
7544 isDirty : function(){
7546 var items = this.getItems();
7547 items.each(function(f){
7557 * Performs a predefined action (submit or load) or custom actions you define on this form.
7558 * @param {String} actionName The name of the action type
7559 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7560 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7561 * accept other config options):
7563 Property Type Description
7564 ---------------- --------------- ----------------------------------------------------------------------------------
7565 url String The url for the action (defaults to the form's url)
7566 method String The form method to use (defaults to the form's method, or POST if not defined)
7567 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7568 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7569 validate the form on the client (defaults to false)
7571 * @return {BasicForm} this
7573 doAction : function(action, options){
7574 if(typeof action == 'string'){
7575 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7577 if(this.fireEvent('beforeaction', this, action) !== false){
7578 this.beforeAction(action);
7579 action.run.defer(100, action);
7585 beforeAction : function(action){
7586 var o = action.options;
7589 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7591 // not really supported yet.. ??
7593 //if(this.waitMsgTarget === true){
7594 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7595 //}else if(this.waitMsgTarget){
7596 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7597 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7599 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7605 afterAction : function(action, success){
7606 this.activeAction = null;
7607 var o = action.options;
7609 //if(this.waitMsgTarget === true){
7611 //}else if(this.waitMsgTarget){
7612 // this.waitMsgTarget.unmask();
7614 // Roo.MessageBox.updateProgress(1);
7615 // Roo.MessageBox.hide();
7622 Roo.callback(o.success, o.scope, [this, action]);
7623 this.fireEvent('actioncomplete', this, action);
7627 // failure condition..
7628 // we have a scenario where updates need confirming.
7629 // eg. if a locking scenario exists..
7630 // we look for { errors : { needs_confirm : true }} in the response.
7632 (typeof(action.result) != 'undefined') &&
7633 (typeof(action.result.errors) != 'undefined') &&
7634 (typeof(action.result.errors.needs_confirm) != 'undefined')
7637 Roo.log("not supported yet");
7640 Roo.MessageBox.confirm(
7641 "Change requires confirmation",
7642 action.result.errorMsg,
7647 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7657 Roo.callback(o.failure, o.scope, [this, action]);
7658 // show an error message if no failed handler is set..
7659 if (!this.hasListener('actionfailed')) {
7660 Roo.log("need to add dialog support");
7662 Roo.MessageBox.alert("Error",
7663 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7664 action.result.errorMsg :
7665 "Saving Failed, please check your entries or try again"
7670 this.fireEvent('actionfailed', this, action);
7675 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7676 * @param {String} id The value to search for
7679 findField : function(id){
7680 var items = this.getItems();
7681 var field = items.get(id);
7683 items.each(function(f){
7684 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7691 return field || null;
7694 * Mark fields in this form invalid in bulk.
7695 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7696 * @return {BasicForm} this
7698 markInvalid : function(errors){
7699 if(errors instanceof Array){
7700 for(var i = 0, len = errors.length; i < len; i++){
7701 var fieldError = errors[i];
7702 var f = this.findField(fieldError.id);
7704 f.markInvalid(fieldError.msg);
7710 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7711 field.markInvalid(errors[id]);
7715 //Roo.each(this.childForms || [], function (f) {
7716 // f.markInvalid(errors);
7723 * Set values for fields in this form in bulk.
7724 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7725 * @return {BasicForm} this
7727 setValues : function(values){
7728 if(values instanceof Array){ // array of objects
7729 for(var i = 0, len = values.length; i < len; i++){
7731 var f = this.findField(v.id);
7733 f.setValue(v.value);
7734 if(this.trackResetOnLoad){
7735 f.originalValue = f.getValue();
7739 }else{ // object hash
7742 if(typeof values[id] != 'function' && (field = this.findField(id))){
7744 if (field.setFromData &&
7746 field.displayField &&
7747 // combos' with local stores can
7748 // be queried via setValue()
7749 // to set their value..
7750 (field.store && !field.store.isLocal)
7754 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7755 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7756 field.setFromData(sd);
7759 field.setValue(values[id]);
7763 if(this.trackResetOnLoad){
7764 field.originalValue = field.getValue();
7770 //Roo.each(this.childForms || [], function (f) {
7771 // f.setValues(values);
7778 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7779 * they are returned as an array.
7780 * @param {Boolean} asString
7783 getValues : function(asString){
7784 //if (this.childForms) {
7785 // copy values from the child forms
7786 // Roo.each(this.childForms, function (f) {
7787 // this.setValues(f.getValues());
7793 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7794 if(asString === true){
7797 return Roo.urlDecode(fs);
7801 * Returns the fields in this form as an object with key/value pairs.
7802 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7805 getFieldValues : function(with_hidden)
7807 var items = this.getItems();
7809 items.each(function(f){
7813 var v = f.getValue();
7814 if (f.inputType =='radio') {
7815 if (typeof(ret[f.getName()]) == 'undefined') {
7816 ret[f.getName()] = ''; // empty..
7819 if (!f.el.dom.checked) {
7827 // not sure if this supported any more..
7828 if ((typeof(v) == 'object') && f.getRawValue) {
7829 v = f.getRawValue() ; // dates..
7831 // combo boxes where name != hiddenName...
7832 if (f.name != f.getName()) {
7833 ret[f.name] = f.getRawValue();
7835 ret[f.getName()] = v;
7842 * Clears all invalid messages in this form.
7843 * @return {BasicForm} this
7845 clearInvalid : function(){
7846 var items = this.getItems();
7848 items.each(function(f){
7859 * @return {BasicForm} this
7862 var items = this.getItems();
7863 items.each(function(f){
7867 Roo.each(this.childForms || [], function (f) {
7874 getItems : function()
7876 var r=new Roo.util.MixedCollection(false, function(o){
7877 return o.id || (o.id = Roo.id());
7879 var iter = function(el) {
7886 Roo.each(el.items,function(e) {
7906 * Ext JS Library 1.1.1
7907 * Copyright(c) 2006-2007, Ext JS, LLC.
7909 * Originally Released Under LGPL - original licence link has changed is not relivant.
7912 * <script type="text/javascript">
7915 * @class Roo.form.VTypes
7916 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7919 Roo.form.VTypes = function(){
7920 // closure these in so they are only created once.
7921 var alpha = /^[a-zA-Z_]+$/;
7922 var alphanum = /^[a-zA-Z0-9_]+$/;
7923 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7924 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7926 // All these messages and functions are configurable
7929 * The function used to validate email addresses
7930 * @param {String} value The email address
7932 'email' : function(v){
7933 return email.test(v);
7936 * The error text to display when the email validation function returns false
7939 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7941 * The keystroke filter mask to be applied on email input
7944 'emailMask' : /[a-z0-9_\.\-@]/i,
7947 * The function used to validate URLs
7948 * @param {String} value The URL
7950 'url' : function(v){
7954 * The error text to display when the url validation function returns false
7957 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7960 * The function used to validate alpha values
7961 * @param {String} value The value
7963 'alpha' : function(v){
7964 return alpha.test(v);
7967 * The error text to display when the alpha validation function returns false
7970 'alphaText' : 'This field should only contain letters and _',
7972 * The keystroke filter mask to be applied on alpha input
7975 'alphaMask' : /[a-z_]/i,
7978 * The function used to validate alphanumeric values
7979 * @param {String} value The value
7981 'alphanum' : function(v){
7982 return alphanum.test(v);
7985 * The error text to display when the alphanumeric validation function returns false
7988 'alphanumText' : 'This field should only contain letters, numbers and _',
7990 * The keystroke filter mask to be applied on alphanumeric input
7993 'alphanumMask' : /[a-z0-9_]/i
8003 * @class Roo.bootstrap.Input
8004 * @extends Roo.bootstrap.Component
8005 * Bootstrap Input class
8006 * @cfg {Boolean} disabled is it disabled
8007 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8008 * @cfg {String} name name of the input
8009 * @cfg {string} fieldLabel - the label associated
8010 * @cfg {string} placeholder - placeholder to put in text.
8011 * @cfg {string} before - input group add on before
8012 * @cfg {string} after - input group add on after
8013 * @cfg {string} size - (lg|sm) or leave empty..
8014 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8015 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8016 * @cfg {Number} md colspan out of 12 for computer-sized screens
8017 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8018 * @cfg {string} value default value of the input
8019 * @cfg {Number} labelWidth set the width of label (0-12)
8020 * @cfg {String} labelAlign (top|left)
8021 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8022 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8023 * @cfg {String} indicatorpos (left|right) default left
8025 * @cfg {String} align (left|center|right) Default left
8026 * @cfg {Boolean} forceFeedback (true|false) Default false
8032 * Create a new Input
8033 * @param {Object} config The config object
8036 Roo.bootstrap.Input = function(config){
8037 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8042 * Fires when this field receives input focus.
8043 * @param {Roo.form.Field} this
8048 * Fires when this field loses input focus.
8049 * @param {Roo.form.Field} this
8054 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8055 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8056 * @param {Roo.form.Field} this
8057 * @param {Roo.EventObject} e The event object
8062 * Fires just before the field blurs if the field value has changed.
8063 * @param {Roo.form.Field} this
8064 * @param {Mixed} newValue The new value
8065 * @param {Mixed} oldValue The original value
8070 * Fires after the field has been marked as invalid.
8071 * @param {Roo.form.Field} this
8072 * @param {String} msg The validation message
8077 * Fires after the field has been validated with no errors.
8078 * @param {Roo.form.Field} this
8083 * Fires after the key up
8084 * @param {Roo.form.Field} this
8085 * @param {Roo.EventObject} e The event Object
8091 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8093 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8094 automatic validation (defaults to "keyup").
8096 validationEvent : "keyup",
8098 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8100 validateOnBlur : true,
8102 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8104 validationDelay : 250,
8106 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8108 focusClass : "x-form-focus", // not needed???
8112 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8114 invalidClass : "has-warning",
8117 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8119 validClass : "has-success",
8122 * @cfg {Boolean} hasFeedback (true|false) default true
8127 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8129 invalidFeedbackClass : "glyphicon-warning-sign",
8132 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8134 validFeedbackClass : "glyphicon-ok",
8137 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8139 selectOnFocus : false,
8142 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8146 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8151 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8153 disableKeyFilter : false,
8156 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8160 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8164 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8166 blankText : "This field is required",
8169 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8173 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8175 maxLength : Number.MAX_VALUE,
8177 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8179 minLengthText : "The minimum length for this field is {0}",
8181 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8183 maxLengthText : "The maximum length for this field is {0}",
8187 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8188 * If available, this function will be called only after the basic validators all return true, and will be passed the
8189 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8193 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8194 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8195 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8199 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8203 autocomplete: false,
8222 formatedValue : false,
8223 forceFeedback : false,
8225 indicatorpos : 'left',
8227 parentLabelAlign : function()
8230 while (parent.parent()) {
8231 parent = parent.parent();
8232 if (typeof(parent.labelAlign) !='undefined') {
8233 return parent.labelAlign;
8240 getAutoCreate : function()
8242 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8248 if(this.inputType != 'hidden'){
8249 cfg.cls = 'form-group' //input-group
8255 type : this.inputType,
8257 cls : 'form-control',
8258 placeholder : this.placeholder || '',
8259 autocomplete : this.autocomplete || 'new-password'
8263 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8266 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8267 input.maxLength = this.maxLength;
8270 if (this.disabled) {
8271 input.disabled=true;
8274 if (this.readOnly) {
8275 input.readonly=true;
8279 input.name = this.name;
8283 input.cls += ' input-' + this.size;
8287 ['xs','sm','md','lg'].map(function(size){
8288 if (settings[size]) {
8289 cfg.cls += ' col-' + size + '-' + settings[size];
8293 var inputblock = input;
8297 cls: 'glyphicon form-control-feedback'
8300 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8303 cls : 'has-feedback',
8311 if (this.before || this.after) {
8314 cls : 'input-group',
8318 if (this.before && typeof(this.before) == 'string') {
8320 inputblock.cn.push({
8322 cls : 'roo-input-before input-group-addon',
8326 if (this.before && typeof(this.before) == 'object') {
8327 this.before = Roo.factory(this.before);
8329 inputblock.cn.push({
8331 cls : 'roo-input-before input-group-' +
8332 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8336 inputblock.cn.push(input);
8338 if (this.after && typeof(this.after) == 'string') {
8339 inputblock.cn.push({
8341 cls : 'roo-input-after input-group-addon',
8345 if (this.after && typeof(this.after) == 'object') {
8346 this.after = Roo.factory(this.after);
8348 inputblock.cn.push({
8350 cls : 'roo-input-after input-group-' +
8351 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8355 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8356 inputblock.cls += ' has-feedback';
8357 inputblock.cn.push(feedback);
8361 if (align ==='left' && this.fieldLabel.length) {
8366 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8367 tooltip : 'This field is required'
8372 cls : 'control-label col-sm-' + this.labelWidth,
8373 html : this.fieldLabel
8377 cls : "col-sm-" + (12 - this.labelWidth),
8385 if(this.indicatorpos == 'right'){
8390 cls : 'control-label col-sm-' + this.labelWidth,
8391 html : this.fieldLabel
8396 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8397 tooltip : 'This field is required'
8400 cls : "col-sm-" + (12 - this.labelWidth),
8409 } else if ( this.fieldLabel.length) {
8414 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8415 tooltip : 'This field is required'
8419 //cls : 'input-group-addon',
8420 html : this.fieldLabel
8428 if(this.indicatorpos == 'right'){
8433 //cls : 'input-group-addon',
8434 html : this.fieldLabel
8439 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8440 tooltip : 'This field is required'
8460 if (this.parentType === 'Navbar' && this.parent().bar) {
8461 cfg.cls += ' navbar-form';
8464 if (this.parentType === 'NavGroup') {
8465 cfg.cls += ' navbar-form';
8473 * return the real input element.
8475 inputEl: function ()
8477 return this.el.select('input.form-control',true).first();
8480 tooltipEl : function()
8482 return this.inputEl();
8485 indicatorEl : function()
8487 var indicator = this.el.select('i.roo-required-indicator',true).first();
8497 setDisabled : function(v)
8499 var i = this.inputEl().dom;
8501 i.removeAttribute('disabled');
8505 i.setAttribute('disabled','true');
8507 initEvents : function()
8510 this.inputEl().on("keydown" , this.fireKey, this);
8511 this.inputEl().on("focus", this.onFocus, this);
8512 this.inputEl().on("blur", this.onBlur, this);
8514 this.inputEl().relayEvent('keyup', this);
8516 this.indicator = this.indicatorEl();
8519 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8520 this.indicator.hide();
8523 // reference to original value for reset
8524 this.originalValue = this.getValue();
8525 //Roo.form.TextField.superclass.initEvents.call(this);
8526 if(this.validationEvent == 'keyup'){
8527 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8528 this.inputEl().on('keyup', this.filterValidation, this);
8530 else if(this.validationEvent !== false){
8531 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8534 if(this.selectOnFocus){
8535 this.on("focus", this.preFocus, this);
8538 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8539 this.inputEl().on("keypress", this.filterKeys, this);
8541 this.inputEl().relayEvent('keypress', this);
8544 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8545 this.el.on("click", this.autoSize, this);
8548 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8549 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8552 if (typeof(this.before) == 'object') {
8553 this.before.render(this.el.select('.roo-input-before',true).first());
8555 if (typeof(this.after) == 'object') {
8556 this.after.render(this.el.select('.roo-input-after',true).first());
8561 filterValidation : function(e){
8562 if(!e.isNavKeyPress()){
8563 this.validationTask.delay(this.validationDelay);
8567 * Validates the field value
8568 * @return {Boolean} True if the value is valid, else false
8570 validate : function(){
8571 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8572 if(this.disabled || this.validateValue(this.getRawValue())){
8583 * Validates a value according to the field's validation rules and marks the field as invalid
8584 * if the validation fails
8585 * @param {Mixed} value The value to validate
8586 * @return {Boolean} True if the value is valid, else false
8588 validateValue : function(value){
8589 if(value.length < 1) { // if it's blank
8590 if(this.allowBlank){
8596 if(value.length < this.minLength){
8599 if(value.length > this.maxLength){
8603 var vt = Roo.form.VTypes;
8604 if(!vt[this.vtype](value, this)){
8608 if(typeof this.validator == "function"){
8609 var msg = this.validator(value);
8615 if(this.regex && !this.regex.test(value)){
8625 fireKey : function(e){
8626 //Roo.log('field ' + e.getKey());
8627 if(e.isNavKeyPress()){
8628 this.fireEvent("specialkey", this, e);
8631 focus : function (selectText){
8633 this.inputEl().focus();
8634 if(selectText === true){
8635 this.inputEl().dom.select();
8641 onFocus : function(){
8642 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8643 // this.el.addClass(this.focusClass);
8646 this.hasFocus = true;
8647 this.startValue = this.getValue();
8648 this.fireEvent("focus", this);
8652 beforeBlur : Roo.emptyFn,
8656 onBlur : function(){
8658 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8659 //this.el.removeClass(this.focusClass);
8661 this.hasFocus = false;
8662 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8665 var v = this.getValue();
8666 if(String(v) !== String(this.startValue)){
8667 this.fireEvent('change', this, v, this.startValue);
8669 this.fireEvent("blur", this);
8673 * Resets the current field value to the originally loaded value and clears any validation messages
8676 this.setValue(this.originalValue);
8680 * Returns the name of the field
8681 * @return {Mixed} name The name field
8683 getName: function(){
8687 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8688 * @return {Mixed} value The field value
8690 getValue : function(){
8692 var v = this.inputEl().getValue();
8697 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8698 * @return {Mixed} value The field value
8700 getRawValue : function(){
8701 var v = this.inputEl().getValue();
8707 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8708 * @param {Mixed} value The value to set
8710 setRawValue : function(v){
8711 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8714 selectText : function(start, end){
8715 var v = this.getRawValue();
8717 start = start === undefined ? 0 : start;
8718 end = end === undefined ? v.length : end;
8719 var d = this.inputEl().dom;
8720 if(d.setSelectionRange){
8721 d.setSelectionRange(start, end);
8722 }else if(d.createTextRange){
8723 var range = d.createTextRange();
8724 range.moveStart("character", start);
8725 range.moveEnd("character", v.length-end);
8732 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8733 * @param {Mixed} value The value to set
8735 setValue : function(v){
8738 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8744 processValue : function(value){
8745 if(this.stripCharsRe){
8746 var newValue = value.replace(this.stripCharsRe, '');
8747 if(newValue !== value){
8748 this.setRawValue(newValue);
8755 preFocus : function(){
8757 if(this.selectOnFocus){
8758 this.inputEl().dom.select();
8761 filterKeys : function(e){
8763 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8766 var c = e.getCharCode(), cc = String.fromCharCode(c);
8767 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8770 if(!this.maskRe.test(cc)){
8775 * Clear any invalid styles/messages for this field
8777 clearInvalid : function(){
8779 if(!this.el || this.preventMark){ // not rendered
8784 this.indicator.hide();
8787 this.el.removeClass(this.invalidClass);
8789 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8791 var feedback = this.el.select('.form-control-feedback', true).first();
8794 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8799 this.fireEvent('valid', this);
8803 * Mark this field as valid
8805 markValid : function()
8807 if(!this.el || this.preventMark){ // not rendered
8811 this.el.removeClass([this.invalidClass, this.validClass]);
8813 var feedback = this.el.select('.form-control-feedback', true).first();
8816 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8819 if(this.disabled || this.allowBlank){
8824 this.indicator.hide();
8827 this.el.addClass(this.validClass);
8829 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8831 var feedback = this.el.select('.form-control-feedback', true).first();
8834 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8835 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8840 this.fireEvent('valid', this);
8844 * Mark this field as invalid
8845 * @param {String} msg The validation message
8847 markInvalid : function(msg)
8849 if(!this.el || this.preventMark){ // not rendered
8853 this.el.removeClass([this.invalidClass, this.validClass]);
8855 var feedback = this.el.select('.form-control-feedback', true).first();
8858 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8861 if(this.disabled || this.allowBlank){
8866 this.indicator.show();
8869 this.el.addClass(this.invalidClass);
8871 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8873 var feedback = this.el.select('.form-control-feedback', true).first();
8876 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8878 if(this.getValue().length || this.forceFeedback){
8879 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8886 this.fireEvent('invalid', this, msg);
8889 SafariOnKeyDown : function(event)
8891 // this is a workaround for a password hang bug on chrome/ webkit.
8893 var isSelectAll = false;
8895 if(this.inputEl().dom.selectionEnd > 0){
8896 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8898 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8899 event.preventDefault();
8904 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8906 event.preventDefault();
8907 // this is very hacky as keydown always get's upper case.
8909 var cc = String.fromCharCode(event.getCharCode());
8910 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8914 adjustWidth : function(tag, w){
8915 tag = tag.toLowerCase();
8916 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8917 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8921 if(tag == 'textarea'){
8924 }else if(Roo.isOpera){
8928 if(tag == 'textarea'){
8947 * @class Roo.bootstrap.TextArea
8948 * @extends Roo.bootstrap.Input
8949 * Bootstrap TextArea class
8950 * @cfg {Number} cols Specifies the visible width of a text area
8951 * @cfg {Number} rows Specifies the visible number of lines in a text area
8952 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8953 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8954 * @cfg {string} html text
8957 * Create a new TextArea
8958 * @param {Object} config The config object
8961 Roo.bootstrap.TextArea = function(config){
8962 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8966 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8976 getAutoCreate : function(){
8978 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8989 value : this.value || '',
8990 html: this.html || '',
8991 cls : 'form-control',
8992 placeholder : this.placeholder || ''
8996 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8997 input.maxLength = this.maxLength;
9001 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9005 input.cols = this.cols;
9008 if (this.readOnly) {
9009 input.readonly = true;
9013 input.name = this.name;
9017 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9021 ['xs','sm','md','lg'].map(function(size){
9022 if (settings[size]) {
9023 cfg.cls += ' col-' + size + '-' + settings[size];
9027 var inputblock = input;
9029 if(this.hasFeedback && !this.allowBlank){
9033 cls: 'glyphicon form-control-feedback'
9037 cls : 'has-feedback',
9046 if (this.before || this.after) {
9049 cls : 'input-group',
9053 inputblock.cn.push({
9055 cls : 'input-group-addon',
9060 inputblock.cn.push(input);
9062 if(this.hasFeedback && !this.allowBlank){
9063 inputblock.cls += ' has-feedback';
9064 inputblock.cn.push(feedback);
9068 inputblock.cn.push({
9070 cls : 'input-group-addon',
9077 if (align ==='left' && this.fieldLabel.length) {
9078 // Roo.log("left and has label");
9084 cls : 'control-label col-sm-' + this.labelWidth,
9085 html : this.fieldLabel
9089 cls : "col-sm-" + (12 - this.labelWidth),
9096 } else if ( this.fieldLabel.length) {
9097 // Roo.log(" label");
9102 //cls : 'input-group-addon',
9103 html : this.fieldLabel
9113 // Roo.log(" no label && no align");
9123 if (this.disabled) {
9124 input.disabled=true;
9131 * return the real textarea element.
9133 inputEl: function ()
9135 return this.el.select('textarea.form-control',true).first();
9139 * Clear any invalid styles/messages for this field
9141 clearInvalid : function()
9144 if(!this.el || this.preventMark){ // not rendered
9148 var label = this.el.select('label', true).first();
9149 var icon = this.el.select('i.fa-star', true).first();
9155 this.el.removeClass(this.invalidClass);
9157 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9159 var feedback = this.el.select('.form-control-feedback', true).first();
9162 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9167 this.fireEvent('valid', this);
9171 * Mark this field as valid
9173 markValid : function()
9175 if(!this.el || this.preventMark){ // not rendered
9179 this.el.removeClass([this.invalidClass, this.validClass]);
9181 var feedback = this.el.select('.form-control-feedback', true).first();
9184 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9187 if(this.disabled || this.allowBlank){
9191 var label = this.el.select('label', true).first();
9192 var icon = this.el.select('i.fa-star', true).first();
9198 this.el.addClass(this.validClass);
9200 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9202 var feedback = this.el.select('.form-control-feedback', true).first();
9205 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9206 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9211 this.fireEvent('valid', this);
9215 * Mark this field as invalid
9216 * @param {String} msg The validation message
9218 markInvalid : function(msg)
9220 if(!this.el || this.preventMark){ // not rendered
9224 this.el.removeClass([this.invalidClass, this.validClass]);
9226 var feedback = this.el.select('.form-control-feedback', true).first();
9229 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9232 if(this.disabled || this.allowBlank){
9236 var label = this.el.select('label', true).first();
9237 var icon = this.el.select('i.fa-star', true).first();
9239 if(!this.getValue().length && label && !icon){
9240 this.el.createChild({
9242 cls : 'text-danger fa fa-lg fa-star',
9243 tooltip : 'This field is required',
9244 style : 'margin-right:5px;'
9248 this.el.addClass(this.invalidClass);
9250 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9252 var feedback = this.el.select('.form-control-feedback', true).first();
9255 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9257 if(this.getValue().length || this.forceFeedback){
9258 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9265 this.fireEvent('invalid', this, msg);
9273 * trigger field - base class for combo..
9278 * @class Roo.bootstrap.TriggerField
9279 * @extends Roo.bootstrap.Input
9280 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9281 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9282 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9283 * for which you can provide a custom implementation. For example:
9285 var trigger = new Roo.bootstrap.TriggerField();
9286 trigger.onTriggerClick = myTriggerFn;
9287 trigger.applyTo('my-field');
9290 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9291 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9292 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9293 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9294 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9297 * Create a new TriggerField.
9298 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9299 * to the base TextField)
9301 Roo.bootstrap.TriggerField = function(config){
9302 this.mimicing = false;
9303 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9306 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9308 * @cfg {String} triggerClass A CSS class to apply to the trigger
9311 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9316 * @cfg {Boolean} removable (true|false) special filter default false
9320 /** @cfg {Boolean} grow @hide */
9321 /** @cfg {Number} growMin @hide */
9322 /** @cfg {Number} growMax @hide */
9328 autoSize: Roo.emptyFn,
9335 actionMode : 'wrap',
9340 getAutoCreate : function(){
9342 var align = this.labelAlign || this.parentLabelAlign();
9347 cls: 'form-group' //input-group
9354 type : this.inputType,
9355 cls : 'form-control',
9356 autocomplete: 'new-password',
9357 placeholder : this.placeholder || ''
9361 input.name = this.name;
9364 input.cls += ' input-' + this.size;
9367 if (this.disabled) {
9368 input.disabled=true;
9371 var inputblock = input;
9373 if(this.hasFeedback && !this.allowBlank){
9377 cls: 'glyphicon form-control-feedback'
9380 if(this.removable && !this.editable && !this.tickable){
9382 cls : 'has-feedback',
9388 cls : 'roo-combo-removable-btn close'
9395 cls : 'has-feedback',
9404 if(this.removable && !this.editable && !this.tickable){
9406 cls : 'roo-removable',
9412 cls : 'roo-combo-removable-btn close'
9419 if (this.before || this.after) {
9422 cls : 'input-group',
9426 inputblock.cn.push({
9428 cls : 'input-group-addon',
9433 inputblock.cn.push(input);
9435 if(this.hasFeedback && !this.allowBlank){
9436 inputblock.cls += ' has-feedback';
9437 inputblock.cn.push(feedback);
9441 inputblock.cn.push({
9443 cls : 'input-group-addon',
9456 cls: 'form-hidden-field'
9470 cls: 'form-hidden-field'
9474 cls: 'roo-select2-choices',
9478 cls: 'roo-select2-search-field',
9491 cls: 'roo-select2-container input-group',
9496 // cls: 'typeahead typeahead-long dropdown-menu',
9497 // style: 'display:none'
9502 if(!this.multiple && this.showToggleBtn){
9508 if (this.caret != false) {
9511 cls: 'fa fa-' + this.caret
9518 cls : 'input-group-addon btn dropdown-toggle',
9523 cls: 'combobox-clear',
9537 combobox.cls += ' roo-select2-container-multi';
9540 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9542 // Roo.log("left and has label");
9546 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9547 tooltip : 'This field is required'
9552 cls : 'control-label col-sm-' + this.labelWidth,
9553 html : this.fieldLabel
9557 cls : "col-sm-" + (12 - this.labelWidth),
9565 if(this.indicatorpos == 'right'){
9570 cls : 'control-label col-sm-' + this.labelWidth,
9571 html : this.fieldLabel
9576 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9577 tooltip : 'This field is required'
9580 cls : "col-sm-" + (12 - this.labelWidth),
9589 } else if ( this.fieldLabel.length) {
9590 // Roo.log(" label");
9594 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9595 tooltip : 'This field is required'
9599 //cls : 'input-group-addon',
9600 html : this.fieldLabel
9608 if(this.indicatorpos == 'right'){
9613 //cls : 'input-group-addon',
9614 html : this.fieldLabel
9619 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9620 tooltip : 'This field is required'
9631 // Roo.log(" no label && no align");
9638 ['xs','sm','md','lg'].map(function(size){
9639 if (settings[size]) {
9640 cfg.cls += ' col-' + size + '-' + settings[size];
9651 onResize : function(w, h){
9652 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9653 // if(typeof w == 'number'){
9654 // var x = w - this.trigger.getWidth();
9655 // this.inputEl().setWidth(this.adjustWidth('input', x));
9656 // this.trigger.setStyle('left', x+'px');
9661 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9664 getResizeEl : function(){
9665 return this.inputEl();
9669 getPositionEl : function(){
9670 return this.inputEl();
9674 alignErrorIcon : function(){
9675 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9679 initEvents : function(){
9683 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9684 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9685 if(!this.multiple && this.showToggleBtn){
9686 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9687 if(this.hideTrigger){
9688 this.trigger.setDisplayed(false);
9690 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9694 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9697 if(this.removable && !this.editable && !this.tickable){
9698 var close = this.closeTriggerEl();
9701 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9702 close.on('click', this.removeBtnClick, this, close);
9706 //this.trigger.addClassOnOver('x-form-trigger-over');
9707 //this.trigger.addClassOnClick('x-form-trigger-click');
9710 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9714 closeTriggerEl : function()
9716 var close = this.el.select('.roo-combo-removable-btn', true).first();
9717 return close ? close : false;
9720 removeBtnClick : function(e, h, el)
9724 if(this.fireEvent("remove", this) !== false){
9726 this.fireEvent("afterremove", this)
9730 createList : function()
9732 this.list = Roo.get(document.body).createChild({
9734 cls: 'typeahead typeahead-long dropdown-menu',
9735 style: 'display:none'
9738 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9743 initTrigger : function(){
9748 onDestroy : function(){
9750 this.trigger.removeAllListeners();
9751 // this.trigger.remove();
9754 // this.wrap.remove();
9756 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9760 onFocus : function(){
9761 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9764 this.wrap.addClass('x-trigger-wrap-focus');
9765 this.mimicing = true;
9766 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9767 if(this.monitorTab){
9768 this.el.on("keydown", this.checkTab, this);
9775 checkTab : function(e){
9776 if(e.getKey() == e.TAB){
9782 onBlur : function(){
9787 mimicBlur : function(e, t){
9789 if(!this.wrap.contains(t) && this.validateBlur()){
9796 triggerBlur : function(){
9797 this.mimicing = false;
9798 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9799 if(this.monitorTab){
9800 this.el.un("keydown", this.checkTab, this);
9802 //this.wrap.removeClass('x-trigger-wrap-focus');
9803 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9807 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9808 validateBlur : function(e, t){
9813 onDisable : function(){
9814 this.inputEl().dom.disabled = true;
9815 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9817 // this.wrap.addClass('x-item-disabled');
9822 onEnable : function(){
9823 this.inputEl().dom.disabled = false;
9824 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9826 // this.el.removeClass('x-item-disabled');
9831 onShow : function(){
9832 var ae = this.getActionEl();
9835 ae.dom.style.display = '';
9836 ae.dom.style.visibility = 'visible';
9842 onHide : function(){
9843 var ae = this.getActionEl();
9844 ae.dom.style.display = 'none';
9848 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9849 * by an implementing function.
9851 * @param {EventObject} e
9853 onTriggerClick : Roo.emptyFn
9857 * Ext JS Library 1.1.1
9858 * Copyright(c) 2006-2007, Ext JS, LLC.
9860 * Originally Released Under LGPL - original licence link has changed is not relivant.
9863 * <script type="text/javascript">
9868 * @class Roo.data.SortTypes
9870 * Defines the default sorting (casting?) comparison functions used when sorting data.
9872 Roo.data.SortTypes = {
9874 * Default sort that does nothing
9875 * @param {Mixed} s The value being converted
9876 * @return {Mixed} The comparison value
9883 * The regular expression used to strip tags
9887 stripTagsRE : /<\/?[^>]+>/gi,
9890 * Strips all HTML tags to sort on text only
9891 * @param {Mixed} s The value being converted
9892 * @return {String} The comparison value
9894 asText : function(s){
9895 return String(s).replace(this.stripTagsRE, "");
9899 * Strips all HTML tags to sort on text only - Case insensitive
9900 * @param {Mixed} s The value being converted
9901 * @return {String} The comparison value
9903 asUCText : function(s){
9904 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9908 * Case insensitive string
9909 * @param {Mixed} s The value being converted
9910 * @return {String} The comparison value
9912 asUCString : function(s) {
9913 return String(s).toUpperCase();
9918 * @param {Mixed} s The value being converted
9919 * @return {Number} The comparison value
9921 asDate : function(s) {
9925 if(s instanceof Date){
9928 return Date.parse(String(s));
9933 * @param {Mixed} s The value being converted
9934 * @return {Float} The comparison value
9936 asFloat : function(s) {
9937 var val = parseFloat(String(s).replace(/,/g, ""));
9946 * @param {Mixed} s The value being converted
9947 * @return {Number} The comparison value
9949 asInt : function(s) {
9950 var val = parseInt(String(s).replace(/,/g, ""));
9958 * Ext JS Library 1.1.1
9959 * Copyright(c) 2006-2007, Ext JS, LLC.
9961 * Originally Released Under LGPL - original licence link has changed is not relivant.
9964 * <script type="text/javascript">
9968 * @class Roo.data.Record
9969 * Instances of this class encapsulate both record <em>definition</em> information, and record
9970 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9971 * to access Records cached in an {@link Roo.data.Store} object.<br>
9973 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9974 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9977 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9979 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9980 * {@link #create}. The parameters are the same.
9981 * @param {Array} data An associative Array of data values keyed by the field name.
9982 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9983 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9984 * not specified an integer id is generated.
9986 Roo.data.Record = function(data, id){
9987 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9992 * Generate a constructor for a specific record layout.
9993 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9994 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9995 * Each field definition object may contain the following properties: <ul>
9996 * <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,
9997 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9998 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9999 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10000 * is being used, then this is a string containing the javascript expression to reference the data relative to
10001 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10002 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10003 * this may be omitted.</p></li>
10004 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10005 * <ul><li>auto (Default, implies no conversion)</li>
10010 * <li>date</li></ul></p></li>
10011 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10012 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10013 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10014 * by the Reader into an object that will be stored in the Record. It is passed the
10015 * following parameters:<ul>
10016 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10018 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10020 * <br>usage:<br><pre><code>
10021 var TopicRecord = Roo.data.Record.create(
10022 {name: 'title', mapping: 'topic_title'},
10023 {name: 'author', mapping: 'username'},
10024 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10025 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10026 {name: 'lastPoster', mapping: 'user2'},
10027 {name: 'excerpt', mapping: 'post_text'}
10030 var myNewRecord = new TopicRecord({
10031 title: 'Do my job please',
10034 lastPost: new Date(),
10035 lastPoster: 'Animal',
10036 excerpt: 'No way dude!'
10038 myStore.add(myNewRecord);
10043 Roo.data.Record.create = function(o){
10044 var f = function(){
10045 f.superclass.constructor.apply(this, arguments);
10047 Roo.extend(f, Roo.data.Record);
10048 var p = f.prototype;
10049 p.fields = new Roo.util.MixedCollection(false, function(field){
10052 for(var i = 0, len = o.length; i < len; i++){
10053 p.fields.add(new Roo.data.Field(o[i]));
10055 f.getField = function(name){
10056 return p.fields.get(name);
10061 Roo.data.Record.AUTO_ID = 1000;
10062 Roo.data.Record.EDIT = 'edit';
10063 Roo.data.Record.REJECT = 'reject';
10064 Roo.data.Record.COMMIT = 'commit';
10066 Roo.data.Record.prototype = {
10068 * Readonly flag - true if this record has been modified.
10077 join : function(store){
10078 this.store = store;
10082 * Set the named field to the specified value.
10083 * @param {String} name The name of the field to set.
10084 * @param {Object} value The value to set the field to.
10086 set : function(name, value){
10087 if(this.data[name] == value){
10091 if(!this.modified){
10092 this.modified = {};
10094 if(typeof this.modified[name] == 'undefined'){
10095 this.modified[name] = this.data[name];
10097 this.data[name] = value;
10098 if(!this.editing && this.store){
10099 this.store.afterEdit(this);
10104 * Get the value of the named field.
10105 * @param {String} name The name of the field to get the value of.
10106 * @return {Object} The value of the field.
10108 get : function(name){
10109 return this.data[name];
10113 beginEdit : function(){
10114 this.editing = true;
10115 this.modified = {};
10119 cancelEdit : function(){
10120 this.editing = false;
10121 delete this.modified;
10125 endEdit : function(){
10126 this.editing = false;
10127 if(this.dirty && this.store){
10128 this.store.afterEdit(this);
10133 * Usually called by the {@link Roo.data.Store} which owns the Record.
10134 * Rejects all changes made to the Record since either creation, or the last commit operation.
10135 * Modified fields are reverted to their original values.
10137 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10138 * of reject operations.
10140 reject : function(){
10141 var m = this.modified;
10143 if(typeof m[n] != "function"){
10144 this.data[n] = m[n];
10147 this.dirty = false;
10148 delete this.modified;
10149 this.editing = false;
10151 this.store.afterReject(this);
10156 * Usually called by the {@link Roo.data.Store} which owns the Record.
10157 * Commits all changes made to the Record since either creation, or the last commit operation.
10159 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10160 * of commit operations.
10162 commit : function(){
10163 this.dirty = false;
10164 delete this.modified;
10165 this.editing = false;
10167 this.store.afterCommit(this);
10172 hasError : function(){
10173 return this.error != null;
10177 clearError : function(){
10182 * Creates a copy of this record.
10183 * @param {String} id (optional) A new record id if you don't want to use this record's id
10186 copy : function(newId) {
10187 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10191 * Ext JS Library 1.1.1
10192 * Copyright(c) 2006-2007, Ext JS, LLC.
10194 * Originally Released Under LGPL - original licence link has changed is not relivant.
10197 * <script type="text/javascript">
10203 * @class Roo.data.Store
10204 * @extends Roo.util.Observable
10205 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10206 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10208 * 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
10209 * has no knowledge of the format of the data returned by the Proxy.<br>
10211 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10212 * instances from the data object. These records are cached and made available through accessor functions.
10214 * Creates a new Store.
10215 * @param {Object} config A config object containing the objects needed for the Store to access data,
10216 * and read the data into Records.
10218 Roo.data.Store = function(config){
10219 this.data = new Roo.util.MixedCollection(false);
10220 this.data.getKey = function(o){
10223 this.baseParams = {};
10225 this.paramNames = {
10230 "multisort" : "_multisort"
10233 if(config && config.data){
10234 this.inlineData = config.data;
10235 delete config.data;
10238 Roo.apply(this, config);
10240 if(this.reader){ // reader passed
10241 this.reader = Roo.factory(this.reader, Roo.data);
10242 this.reader.xmodule = this.xmodule || false;
10243 if(!this.recordType){
10244 this.recordType = this.reader.recordType;
10246 if(this.reader.onMetaChange){
10247 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10251 if(this.recordType){
10252 this.fields = this.recordType.prototype.fields;
10254 this.modified = [];
10258 * @event datachanged
10259 * Fires when the data cache has changed, and a widget which is using this Store
10260 * as a Record cache should refresh its view.
10261 * @param {Store} this
10263 datachanged : true,
10265 * @event metachange
10266 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10267 * @param {Store} this
10268 * @param {Object} meta The JSON metadata
10273 * Fires when Records have been added to the Store
10274 * @param {Store} this
10275 * @param {Roo.data.Record[]} records The array of Records added
10276 * @param {Number} index The index at which the record(s) were added
10281 * Fires when a Record has been removed from the Store
10282 * @param {Store} this
10283 * @param {Roo.data.Record} record The Record that was removed
10284 * @param {Number} index The index at which the record was removed
10289 * Fires when a Record has been updated
10290 * @param {Store} this
10291 * @param {Roo.data.Record} record The Record that was updated
10292 * @param {String} operation The update operation being performed. Value may be one of:
10294 Roo.data.Record.EDIT
10295 Roo.data.Record.REJECT
10296 Roo.data.Record.COMMIT
10302 * Fires when the data cache has been cleared.
10303 * @param {Store} this
10307 * @event beforeload
10308 * Fires before a request is made for a new data object. If the beforeload handler returns false
10309 * the load action will be canceled.
10310 * @param {Store} this
10311 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10315 * @event beforeloadadd
10316 * Fires after a new set of Records has been loaded.
10317 * @param {Store} this
10318 * @param {Roo.data.Record[]} records The Records that were loaded
10319 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10321 beforeloadadd : true,
10324 * Fires after a new set of Records has been loaded, before they are added to the store.
10325 * @param {Store} this
10326 * @param {Roo.data.Record[]} records The Records that were loaded
10327 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10328 * @params {Object} return from reader
10332 * @event loadexception
10333 * Fires if an exception occurs in the Proxy during loading.
10334 * Called with the signature of the Proxy's "loadexception" event.
10335 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10338 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10339 * @param {Object} load options
10340 * @param {Object} jsonData from your request (normally this contains the Exception)
10342 loadexception : true
10346 this.proxy = Roo.factory(this.proxy, Roo.data);
10347 this.proxy.xmodule = this.xmodule || false;
10348 this.relayEvents(this.proxy, ["loadexception"]);
10350 this.sortToggle = {};
10351 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10353 Roo.data.Store.superclass.constructor.call(this);
10355 if(this.inlineData){
10356 this.loadData(this.inlineData);
10357 delete this.inlineData;
10361 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10363 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10364 * without a remote query - used by combo/forms at present.
10368 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10371 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10374 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10375 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10378 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10379 * on any HTTP request
10382 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10385 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10389 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10390 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10392 remoteSort : false,
10395 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10396 * loaded or when a record is removed. (defaults to false).
10398 pruneModifiedRecords : false,
10401 lastOptions : null,
10404 * Add Records to the Store and fires the add event.
10405 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10407 add : function(records){
10408 records = [].concat(records);
10409 for(var i = 0, len = records.length; i < len; i++){
10410 records[i].join(this);
10412 var index = this.data.length;
10413 this.data.addAll(records);
10414 this.fireEvent("add", this, records, index);
10418 * Remove a Record from the Store and fires the remove event.
10419 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10421 remove : function(record){
10422 var index = this.data.indexOf(record);
10423 this.data.removeAt(index);
10424 if(this.pruneModifiedRecords){
10425 this.modified.remove(record);
10427 this.fireEvent("remove", this, record, index);
10431 * Remove all Records from the Store and fires the clear event.
10433 removeAll : function(){
10435 if(this.pruneModifiedRecords){
10436 this.modified = [];
10438 this.fireEvent("clear", this);
10442 * Inserts Records to the Store at the given index and fires the add event.
10443 * @param {Number} index The start index at which to insert the passed Records.
10444 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10446 insert : function(index, records){
10447 records = [].concat(records);
10448 for(var i = 0, len = records.length; i < len; i++){
10449 this.data.insert(index, records[i]);
10450 records[i].join(this);
10452 this.fireEvent("add", this, records, index);
10456 * Get the index within the cache of the passed Record.
10457 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10458 * @return {Number} The index of the passed Record. Returns -1 if not found.
10460 indexOf : function(record){
10461 return this.data.indexOf(record);
10465 * Get the index within the cache of the Record with the passed id.
10466 * @param {String} id The id of the Record to find.
10467 * @return {Number} The index of the Record. Returns -1 if not found.
10469 indexOfId : function(id){
10470 return this.data.indexOfKey(id);
10474 * Get the Record with the specified id.
10475 * @param {String} id The id of the Record to find.
10476 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10478 getById : function(id){
10479 return this.data.key(id);
10483 * Get the Record at the specified index.
10484 * @param {Number} index The index of the Record to find.
10485 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10487 getAt : function(index){
10488 return this.data.itemAt(index);
10492 * Returns a range of Records between specified indices.
10493 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10494 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10495 * @return {Roo.data.Record[]} An array of Records
10497 getRange : function(start, end){
10498 return this.data.getRange(start, end);
10502 storeOptions : function(o){
10503 o = Roo.apply({}, o);
10506 this.lastOptions = o;
10510 * Loads the Record cache from the configured Proxy using the configured Reader.
10512 * If using remote paging, then the first load call must specify the <em>start</em>
10513 * and <em>limit</em> properties in the options.params property to establish the initial
10514 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10516 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10517 * and this call will return before the new data has been loaded. Perform any post-processing
10518 * in a callback function, or in a "load" event handler.</strong>
10520 * @param {Object} options An object containing properties which control loading options:<ul>
10521 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10522 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10523 * passed the following arguments:<ul>
10524 * <li>r : Roo.data.Record[]</li>
10525 * <li>options: Options object from the load call</li>
10526 * <li>success: Boolean success indicator</li></ul></li>
10527 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10528 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10531 load : function(options){
10532 options = options || {};
10533 if(this.fireEvent("beforeload", this, options) !== false){
10534 this.storeOptions(options);
10535 var p = Roo.apply(options.params || {}, this.baseParams);
10536 // if meta was not loaded from remote source.. try requesting it.
10537 if (!this.reader.metaFromRemote) {
10538 p._requestMeta = 1;
10540 if(this.sortInfo && this.remoteSort){
10541 var pn = this.paramNames;
10542 p[pn["sort"]] = this.sortInfo.field;
10543 p[pn["dir"]] = this.sortInfo.direction;
10545 if (this.multiSort) {
10546 var pn = this.paramNames;
10547 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10550 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10555 * Reloads the Record cache from the configured Proxy using the configured Reader and
10556 * the options from the last load operation performed.
10557 * @param {Object} options (optional) An object containing properties which may override the options
10558 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10559 * the most recently used options are reused).
10561 reload : function(options){
10562 this.load(Roo.applyIf(options||{}, this.lastOptions));
10566 // Called as a callback by the Reader during a load operation.
10567 loadRecords : function(o, options, success){
10568 if(!o || success === false){
10569 if(success !== false){
10570 this.fireEvent("load", this, [], options, o);
10572 if(options.callback){
10573 options.callback.call(options.scope || this, [], options, false);
10577 // if data returned failure - throw an exception.
10578 if (o.success === false) {
10579 // show a message if no listener is registered.
10580 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10581 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10583 // loadmask wil be hooked into this..
10584 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10587 var r = o.records, t = o.totalRecords || r.length;
10589 this.fireEvent("beforeloadadd", this, r, options, o);
10591 if(!options || options.add !== true){
10592 if(this.pruneModifiedRecords){
10593 this.modified = [];
10595 for(var i = 0, len = r.length; i < len; i++){
10599 this.data = this.snapshot;
10600 delete this.snapshot;
10603 this.data.addAll(r);
10604 this.totalLength = t;
10606 this.fireEvent("datachanged", this);
10608 this.totalLength = Math.max(t, this.data.length+r.length);
10611 this.fireEvent("load", this, r, options, o);
10612 if(options.callback){
10613 options.callback.call(options.scope || this, r, options, true);
10619 * Loads data from a passed data block. A Reader which understands the format of the data
10620 * must have been configured in the constructor.
10621 * @param {Object} data The data block from which to read the Records. The format of the data expected
10622 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10623 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10625 loadData : function(o, append){
10626 var r = this.reader.readRecords(o);
10627 this.loadRecords(r, {add: append}, true);
10631 * Gets the number of cached records.
10633 * <em>If using paging, this may not be the total size of the dataset. If the data object
10634 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10635 * the data set size</em>
10637 getCount : function(){
10638 return this.data.length || 0;
10642 * Gets the total number of records in the dataset as returned by the server.
10644 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10645 * the dataset size</em>
10647 getTotalCount : function(){
10648 return this.totalLength || 0;
10652 * Returns the sort state of the Store as an object with two properties:
10654 field {String} The name of the field by which the Records are sorted
10655 direction {String} The sort order, "ASC" or "DESC"
10658 getSortState : function(){
10659 return this.sortInfo;
10663 applySort : function(){
10664 if(this.sortInfo && !this.remoteSort){
10665 var s = this.sortInfo, f = s.field;
10666 var st = this.fields.get(f).sortType;
10667 var fn = function(r1, r2){
10668 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10669 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10671 this.data.sort(s.direction, fn);
10672 if(this.snapshot && this.snapshot != this.data){
10673 this.snapshot.sort(s.direction, fn);
10679 * Sets the default sort column and order to be used by the next load operation.
10680 * @param {String} fieldName The name of the field to sort by.
10681 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10683 setDefaultSort : function(field, dir){
10684 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10688 * Sort the Records.
10689 * If remote sorting is used, the sort is performed on the server, and the cache is
10690 * reloaded. If local sorting is used, the cache is sorted internally.
10691 * @param {String} fieldName The name of the field to sort by.
10692 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10694 sort : function(fieldName, dir){
10695 var f = this.fields.get(fieldName);
10697 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10699 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10700 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10705 this.sortToggle[f.name] = dir;
10706 this.sortInfo = {field: f.name, direction: dir};
10707 if(!this.remoteSort){
10709 this.fireEvent("datachanged", this);
10711 this.load(this.lastOptions);
10716 * Calls the specified function for each of the Records in the cache.
10717 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10718 * Returning <em>false</em> aborts and exits the iteration.
10719 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10721 each : function(fn, scope){
10722 this.data.each(fn, scope);
10726 * Gets all records modified since the last commit. Modified records are persisted across load operations
10727 * (e.g., during paging).
10728 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10730 getModifiedRecords : function(){
10731 return this.modified;
10735 createFilterFn : function(property, value, anyMatch){
10736 if(!value.exec){ // not a regex
10737 value = String(value);
10738 if(value.length == 0){
10741 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10743 return function(r){
10744 return value.test(r.data[property]);
10749 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10750 * @param {String} property A field on your records
10751 * @param {Number} start The record index to start at (defaults to 0)
10752 * @param {Number} end The last record index to include (defaults to length - 1)
10753 * @return {Number} The sum
10755 sum : function(property, start, end){
10756 var rs = this.data.items, v = 0;
10757 start = start || 0;
10758 end = (end || end === 0) ? end : rs.length-1;
10760 for(var i = start; i <= end; i++){
10761 v += (rs[i].data[property] || 0);
10767 * Filter the records by a specified property.
10768 * @param {String} field A field on your records
10769 * @param {String/RegExp} value Either a string that the field
10770 * should start with or a RegExp to test against the field
10771 * @param {Boolean} anyMatch True to match any part not just the beginning
10773 filter : function(property, value, anyMatch){
10774 var fn = this.createFilterFn(property, value, anyMatch);
10775 return fn ? this.filterBy(fn) : this.clearFilter();
10779 * Filter by a function. The specified function will be called with each
10780 * record in this data source. If the function returns true the record is included,
10781 * otherwise it is filtered.
10782 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10783 * @param {Object} scope (optional) The scope of the function (defaults to this)
10785 filterBy : function(fn, scope){
10786 this.snapshot = this.snapshot || this.data;
10787 this.data = this.queryBy(fn, scope||this);
10788 this.fireEvent("datachanged", this);
10792 * Query the records by a specified property.
10793 * @param {String} field A field on your records
10794 * @param {String/RegExp} value Either a string that the field
10795 * should start with or a RegExp to test against the field
10796 * @param {Boolean} anyMatch True to match any part not just the beginning
10797 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10799 query : function(property, value, anyMatch){
10800 var fn = this.createFilterFn(property, value, anyMatch);
10801 return fn ? this.queryBy(fn) : this.data.clone();
10805 * Query by a function. The specified function will be called with each
10806 * record in this data source. If the function returns true the record is included
10808 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10809 * @param {Object} scope (optional) The scope of the function (defaults to this)
10810 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10812 queryBy : function(fn, scope){
10813 var data = this.snapshot || this.data;
10814 return data.filterBy(fn, scope||this);
10818 * Collects unique values for a particular dataIndex from this store.
10819 * @param {String} dataIndex The property to collect
10820 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10821 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10822 * @return {Array} An array of the unique values
10824 collect : function(dataIndex, allowNull, bypassFilter){
10825 var d = (bypassFilter === true && this.snapshot) ?
10826 this.snapshot.items : this.data.items;
10827 var v, sv, r = [], l = {};
10828 for(var i = 0, len = d.length; i < len; i++){
10829 v = d[i].data[dataIndex];
10831 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10840 * Revert to a view of the Record cache with no filtering applied.
10841 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10843 clearFilter : function(suppressEvent){
10844 if(this.snapshot && this.snapshot != this.data){
10845 this.data = this.snapshot;
10846 delete this.snapshot;
10847 if(suppressEvent !== true){
10848 this.fireEvent("datachanged", this);
10854 afterEdit : function(record){
10855 if(this.modified.indexOf(record) == -1){
10856 this.modified.push(record);
10858 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10862 afterReject : function(record){
10863 this.modified.remove(record);
10864 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10868 afterCommit : function(record){
10869 this.modified.remove(record);
10870 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10874 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10875 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10877 commitChanges : function(){
10878 var m = this.modified.slice(0);
10879 this.modified = [];
10880 for(var i = 0, len = m.length; i < len; i++){
10886 * Cancel outstanding changes on all changed records.
10888 rejectChanges : function(){
10889 var m = this.modified.slice(0);
10890 this.modified = [];
10891 for(var i = 0, len = m.length; i < len; i++){
10896 onMetaChange : function(meta, rtype, o){
10897 this.recordType = rtype;
10898 this.fields = rtype.prototype.fields;
10899 delete this.snapshot;
10900 this.sortInfo = meta.sortInfo || this.sortInfo;
10901 this.modified = [];
10902 this.fireEvent('metachange', this, this.reader.meta);
10905 moveIndex : function(data, type)
10907 var index = this.indexOf(data);
10909 var newIndex = index + type;
10913 this.insert(newIndex, data);
10918 * Ext JS Library 1.1.1
10919 * Copyright(c) 2006-2007, Ext JS, LLC.
10921 * Originally Released Under LGPL - original licence link has changed is not relivant.
10924 * <script type="text/javascript">
10928 * @class Roo.data.SimpleStore
10929 * @extends Roo.data.Store
10930 * Small helper class to make creating Stores from Array data easier.
10931 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10932 * @cfg {Array} fields An array of field definition objects, or field name strings.
10933 * @cfg {Array} data The multi-dimensional array of data
10935 * @param {Object} config
10937 Roo.data.SimpleStore = function(config){
10938 Roo.data.SimpleStore.superclass.constructor.call(this, {
10940 reader: new Roo.data.ArrayReader({
10943 Roo.data.Record.create(config.fields)
10945 proxy : new Roo.data.MemoryProxy(config.data)
10949 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10951 * Ext JS Library 1.1.1
10952 * Copyright(c) 2006-2007, Ext JS, LLC.
10954 * Originally Released Under LGPL - original licence link has changed is not relivant.
10957 * <script type="text/javascript">
10962 * @extends Roo.data.Store
10963 * @class Roo.data.JsonStore
10964 * Small helper class to make creating Stores for JSON data easier. <br/>
10966 var store = new Roo.data.JsonStore({
10967 url: 'get-images.php',
10969 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10972 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10973 * JsonReader and HttpProxy (unless inline data is provided).</b>
10974 * @cfg {Array} fields An array of field definition objects, or field name strings.
10976 * @param {Object} config
10978 Roo.data.JsonStore = function(c){
10979 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10980 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10981 reader: new Roo.data.JsonReader(c, c.fields)
10984 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10986 * Ext JS Library 1.1.1
10987 * Copyright(c) 2006-2007, Ext JS, LLC.
10989 * Originally Released Under LGPL - original licence link has changed is not relivant.
10992 * <script type="text/javascript">
10996 Roo.data.Field = function(config){
10997 if(typeof config == "string"){
10998 config = {name: config};
11000 Roo.apply(this, config);
11003 this.type = "auto";
11006 var st = Roo.data.SortTypes;
11007 // named sortTypes are supported, here we look them up
11008 if(typeof this.sortType == "string"){
11009 this.sortType = st[this.sortType];
11012 // set default sortType for strings and dates
11013 if(!this.sortType){
11016 this.sortType = st.asUCString;
11019 this.sortType = st.asDate;
11022 this.sortType = st.none;
11027 var stripRe = /[\$,%]/g;
11029 // prebuilt conversion function for this field, instead of
11030 // switching every time we're reading a value
11032 var cv, dateFormat = this.dateFormat;
11037 cv = function(v){ return v; };
11040 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11044 return v !== undefined && v !== null && v !== '' ?
11045 parseInt(String(v).replace(stripRe, ""), 10) : '';
11050 return v !== undefined && v !== null && v !== '' ?
11051 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11056 cv = function(v){ return v === true || v === "true" || v == 1; };
11063 if(v instanceof Date){
11067 if(dateFormat == "timestamp"){
11068 return new Date(v*1000);
11070 return Date.parseDate(v, dateFormat);
11072 var parsed = Date.parse(v);
11073 return parsed ? new Date(parsed) : null;
11082 Roo.data.Field.prototype = {
11090 * Ext JS Library 1.1.1
11091 * Copyright(c) 2006-2007, Ext JS, LLC.
11093 * Originally Released Under LGPL - original licence link has changed is not relivant.
11096 * <script type="text/javascript">
11099 // Base class for reading structured data from a data source. This class is intended to be
11100 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11103 * @class Roo.data.DataReader
11104 * Base class for reading structured data from a data source. This class is intended to be
11105 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11108 Roo.data.DataReader = function(meta, recordType){
11112 this.recordType = recordType instanceof Array ?
11113 Roo.data.Record.create(recordType) : recordType;
11116 Roo.data.DataReader.prototype = {
11118 * Create an empty record
11119 * @param {Object} data (optional) - overlay some values
11120 * @return {Roo.data.Record} record created.
11122 newRow : function(d) {
11124 this.recordType.prototype.fields.each(function(c) {
11126 case 'int' : da[c.name] = 0; break;
11127 case 'date' : da[c.name] = new Date(); break;
11128 case 'float' : da[c.name] = 0.0; break;
11129 case 'boolean' : da[c.name] = false; break;
11130 default : da[c.name] = ""; break;
11134 return new this.recordType(Roo.apply(da, d));
11139 * Ext JS Library 1.1.1
11140 * Copyright(c) 2006-2007, Ext JS, LLC.
11142 * Originally Released Under LGPL - original licence link has changed is not relivant.
11145 * <script type="text/javascript">
11149 * @class Roo.data.DataProxy
11150 * @extends Roo.data.Observable
11151 * This class is an abstract base class for implementations which provide retrieval of
11152 * unformatted data objects.<br>
11154 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11155 * (of the appropriate type which knows how to parse the data object) to provide a block of
11156 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11158 * Custom implementations must implement the load method as described in
11159 * {@link Roo.data.HttpProxy#load}.
11161 Roo.data.DataProxy = function(){
11164 * @event beforeload
11165 * Fires before a network request is made to retrieve a data object.
11166 * @param {Object} This DataProxy object.
11167 * @param {Object} params The params parameter to the load function.
11172 * Fires before the load method's callback is called.
11173 * @param {Object} This DataProxy object.
11174 * @param {Object} o The data object.
11175 * @param {Object} arg The callback argument object passed to the load function.
11179 * @event loadexception
11180 * Fires if an Exception occurs during data retrieval.
11181 * @param {Object} This DataProxy object.
11182 * @param {Object} o The data object.
11183 * @param {Object} arg The callback argument object passed to the load function.
11184 * @param {Object} e The Exception.
11186 loadexception : true
11188 Roo.data.DataProxy.superclass.constructor.call(this);
11191 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11194 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11198 * Ext JS Library 1.1.1
11199 * Copyright(c) 2006-2007, Ext JS, LLC.
11201 * Originally Released Under LGPL - original licence link has changed is not relivant.
11204 * <script type="text/javascript">
11207 * @class Roo.data.MemoryProxy
11208 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11209 * to the Reader when its load method is called.
11211 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11213 Roo.data.MemoryProxy = function(data){
11217 Roo.data.MemoryProxy.superclass.constructor.call(this);
11221 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11224 * Load data from the requested source (in this case an in-memory
11225 * data object passed to the constructor), read the data object into
11226 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11227 * process that block using the passed callback.
11228 * @param {Object} params This parameter is not used by the MemoryProxy class.
11229 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11230 * object into a block of Roo.data.Records.
11231 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11232 * The function must be passed <ul>
11233 * <li>The Record block object</li>
11234 * <li>The "arg" argument from the load function</li>
11235 * <li>A boolean success indicator</li>
11237 * @param {Object} scope The scope in which to call the callback
11238 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11240 load : function(params, reader, callback, scope, arg){
11241 params = params || {};
11244 result = reader.readRecords(this.data);
11246 this.fireEvent("loadexception", this, arg, null, e);
11247 callback.call(scope, null, arg, false);
11250 callback.call(scope, result, arg, true);
11254 update : function(params, records){
11259 * Ext JS Library 1.1.1
11260 * Copyright(c) 2006-2007, Ext JS, LLC.
11262 * Originally Released Under LGPL - original licence link has changed is not relivant.
11265 * <script type="text/javascript">
11268 * @class Roo.data.HttpProxy
11269 * @extends Roo.data.DataProxy
11270 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11271 * configured to reference a certain URL.<br><br>
11273 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11274 * from which the running page was served.<br><br>
11276 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11278 * Be aware that to enable the browser to parse an XML document, the server must set
11279 * the Content-Type header in the HTTP response to "text/xml".
11281 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11282 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11283 * will be used to make the request.
11285 Roo.data.HttpProxy = function(conn){
11286 Roo.data.HttpProxy.superclass.constructor.call(this);
11287 // is conn a conn config or a real conn?
11289 this.useAjax = !conn || !conn.events;
11293 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11294 // thse are take from connection...
11297 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11300 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11301 * extra parameters to each request made by this object. (defaults to undefined)
11304 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11305 * to each request made by this object. (defaults to undefined)
11308 * @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)
11311 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11314 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11320 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11324 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11325 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11326 * a finer-grained basis than the DataProxy events.
11328 getConnection : function(){
11329 return this.useAjax ? Roo.Ajax : this.conn;
11333 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11334 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11335 * process that block using the passed callback.
11336 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11337 * for the request to the remote server.
11338 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11339 * object into a block of Roo.data.Records.
11340 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11341 * The function must be passed <ul>
11342 * <li>The Record block object</li>
11343 * <li>The "arg" argument from the load function</li>
11344 * <li>A boolean success indicator</li>
11346 * @param {Object} scope The scope in which to call the callback
11347 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11349 load : function(params, reader, callback, scope, arg){
11350 if(this.fireEvent("beforeload", this, params) !== false){
11352 params : params || {},
11354 callback : callback,
11359 callback : this.loadResponse,
11363 Roo.applyIf(o, this.conn);
11364 if(this.activeRequest){
11365 Roo.Ajax.abort(this.activeRequest);
11367 this.activeRequest = Roo.Ajax.request(o);
11369 this.conn.request(o);
11372 callback.call(scope||this, null, arg, false);
11377 loadResponse : function(o, success, response){
11378 delete this.activeRequest;
11380 this.fireEvent("loadexception", this, o, response);
11381 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11386 result = o.reader.read(response);
11388 this.fireEvent("loadexception", this, o, response, e);
11389 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11393 this.fireEvent("load", this, o, o.request.arg);
11394 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11398 update : function(dataSet){
11403 updateResponse : function(dataSet){
11408 * Ext JS Library 1.1.1
11409 * Copyright(c) 2006-2007, Ext JS, LLC.
11411 * Originally Released Under LGPL - original licence link has changed is not relivant.
11414 * <script type="text/javascript">
11418 * @class Roo.data.ScriptTagProxy
11419 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11420 * other than the originating domain of the running page.<br><br>
11422 * <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
11423 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11425 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11426 * source code that is used as the source inside a <script> tag.<br><br>
11428 * In order for the browser to process the returned data, the server must wrap the data object
11429 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11430 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11431 * depending on whether the callback name was passed:
11434 boolean scriptTag = false;
11435 String cb = request.getParameter("callback");
11438 response.setContentType("text/javascript");
11440 response.setContentType("application/x-json");
11442 Writer out = response.getWriter();
11444 out.write(cb + "(");
11446 out.print(dataBlock.toJsonString());
11453 * @param {Object} config A configuration object.
11455 Roo.data.ScriptTagProxy = function(config){
11456 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11457 Roo.apply(this, config);
11458 this.head = document.getElementsByTagName("head")[0];
11461 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11463 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11465 * @cfg {String} url The URL from which to request the data object.
11468 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11472 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11473 * the server the name of the callback function set up by the load call to process the returned data object.
11474 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11475 * javascript output which calls this named function passing the data object as its only parameter.
11477 callbackParam : "callback",
11479 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11480 * name to the request.
11485 * Load data from the configured URL, read the data object into
11486 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11487 * process that block using the passed callback.
11488 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11489 * for the request to the remote server.
11490 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11491 * object into a block of Roo.data.Records.
11492 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11493 * The function must be passed <ul>
11494 * <li>The Record block object</li>
11495 * <li>The "arg" argument from the load function</li>
11496 * <li>A boolean success indicator</li>
11498 * @param {Object} scope The scope in which to call the callback
11499 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11501 load : function(params, reader, callback, scope, arg){
11502 if(this.fireEvent("beforeload", this, params) !== false){
11504 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11506 var url = this.url;
11507 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11509 url += "&_dc=" + (new Date().getTime());
11511 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11514 cb : "stcCallback"+transId,
11515 scriptId : "stcScript"+transId,
11519 callback : callback,
11525 window[trans.cb] = function(o){
11526 conn.handleResponse(o, trans);
11529 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11531 if(this.autoAbort !== false){
11535 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11537 var script = document.createElement("script");
11538 script.setAttribute("src", url);
11539 script.setAttribute("type", "text/javascript");
11540 script.setAttribute("id", trans.scriptId);
11541 this.head.appendChild(script);
11543 this.trans = trans;
11545 callback.call(scope||this, null, arg, false);
11550 isLoading : function(){
11551 return this.trans ? true : false;
11555 * Abort the current server request.
11557 abort : function(){
11558 if(this.isLoading()){
11559 this.destroyTrans(this.trans);
11564 destroyTrans : function(trans, isLoaded){
11565 this.head.removeChild(document.getElementById(trans.scriptId));
11566 clearTimeout(trans.timeoutId);
11568 window[trans.cb] = undefined;
11570 delete window[trans.cb];
11573 // if hasn't been loaded, wait for load to remove it to prevent script error
11574 window[trans.cb] = function(){
11575 window[trans.cb] = undefined;
11577 delete window[trans.cb];
11584 handleResponse : function(o, trans){
11585 this.trans = false;
11586 this.destroyTrans(trans, true);
11589 result = trans.reader.readRecords(o);
11591 this.fireEvent("loadexception", this, o, trans.arg, e);
11592 trans.callback.call(trans.scope||window, null, trans.arg, false);
11595 this.fireEvent("load", this, o, trans.arg);
11596 trans.callback.call(trans.scope||window, result, trans.arg, true);
11600 handleFailure : function(trans){
11601 this.trans = false;
11602 this.destroyTrans(trans, false);
11603 this.fireEvent("loadexception", this, null, trans.arg);
11604 trans.callback.call(trans.scope||window, null, trans.arg, false);
11608 * Ext JS Library 1.1.1
11609 * Copyright(c) 2006-2007, Ext JS, LLC.
11611 * Originally Released Under LGPL - original licence link has changed is not relivant.
11614 * <script type="text/javascript">
11618 * @class Roo.data.JsonReader
11619 * @extends Roo.data.DataReader
11620 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11621 * based on mappings in a provided Roo.data.Record constructor.
11623 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11624 * in the reply previously.
11629 var RecordDef = Roo.data.Record.create([
11630 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11631 {name: 'occupation'} // This field will use "occupation" as the mapping.
11633 var myReader = new Roo.data.JsonReader({
11634 totalProperty: "results", // The property which contains the total dataset size (optional)
11635 root: "rows", // The property which contains an Array of row objects
11636 id: "id" // The property within each row object that provides an ID for the record (optional)
11640 * This would consume a JSON file like this:
11642 { 'results': 2, 'rows': [
11643 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11644 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11647 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11648 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11649 * paged from the remote server.
11650 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11651 * @cfg {String} root name of the property which contains the Array of row objects.
11652 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11653 * @cfg {Array} fields Array of field definition objects
11655 * Create a new JsonReader
11656 * @param {Object} meta Metadata configuration options
11657 * @param {Object} recordType Either an Array of field definition objects,
11658 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11660 Roo.data.JsonReader = function(meta, recordType){
11663 // set some defaults:
11664 Roo.applyIf(meta, {
11665 totalProperty: 'total',
11666 successProperty : 'success',
11671 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11673 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11676 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11677 * Used by Store query builder to append _requestMeta to params.
11680 metaFromRemote : false,
11682 * This method is only used by a DataProxy which has retrieved data from a remote server.
11683 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11684 * @return {Object} data A data block which is used by an Roo.data.Store object as
11685 * a cache of Roo.data.Records.
11687 read : function(response){
11688 var json = response.responseText;
11690 var o = /* eval:var:o */ eval("("+json+")");
11692 throw {message: "JsonReader.read: Json object not found"};
11698 this.metaFromRemote = true;
11699 this.meta = o.metaData;
11700 this.recordType = Roo.data.Record.create(o.metaData.fields);
11701 this.onMetaChange(this.meta, this.recordType, o);
11703 return this.readRecords(o);
11706 // private function a store will implement
11707 onMetaChange : function(meta, recordType, o){
11714 simpleAccess: function(obj, subsc) {
11721 getJsonAccessor: function(){
11723 return function(expr) {
11725 return(re.test(expr))
11726 ? new Function("obj", "return obj." + expr)
11731 return Roo.emptyFn;
11736 * Create a data block containing Roo.data.Records from an XML document.
11737 * @param {Object} o An object which contains an Array of row objects in the property specified
11738 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11739 * which contains the total size of the dataset.
11740 * @return {Object} data A data block which is used by an Roo.data.Store object as
11741 * a cache of Roo.data.Records.
11743 readRecords : function(o){
11745 * After any data loads, the raw JSON data is available for further custom processing.
11749 var s = this.meta, Record = this.recordType,
11750 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11752 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11754 if(s.totalProperty) {
11755 this.getTotal = this.getJsonAccessor(s.totalProperty);
11757 if(s.successProperty) {
11758 this.getSuccess = this.getJsonAccessor(s.successProperty);
11760 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11762 var g = this.getJsonAccessor(s.id);
11763 this.getId = function(rec) {
11765 return (r === undefined || r === "") ? null : r;
11768 this.getId = function(){return null;};
11771 for(var jj = 0; jj < fl; jj++){
11773 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11774 this.ef[jj] = this.getJsonAccessor(map);
11778 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11779 if(s.totalProperty){
11780 var vt = parseInt(this.getTotal(o), 10);
11785 if(s.successProperty){
11786 var vs = this.getSuccess(o);
11787 if(vs === false || vs === 'false'){
11792 for(var i = 0; i < c; i++){
11795 var id = this.getId(n);
11796 for(var j = 0; j < fl; j++){
11798 var v = this.ef[j](n);
11800 Roo.log('missing convert for ' + f.name);
11804 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11806 var record = new Record(values, id);
11808 records[i] = record;
11814 totalRecords : totalRecords
11819 * Ext JS Library 1.1.1
11820 * Copyright(c) 2006-2007, Ext JS, LLC.
11822 * Originally Released Under LGPL - original licence link has changed is not relivant.
11825 * <script type="text/javascript">
11829 * @class Roo.data.ArrayReader
11830 * @extends Roo.data.DataReader
11831 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11832 * Each element of that Array represents a row of data fields. The
11833 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11834 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11838 var RecordDef = Roo.data.Record.create([
11839 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11840 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11842 var myReader = new Roo.data.ArrayReader({
11843 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11847 * This would consume an Array like this:
11849 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11851 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11853 * Create a new JsonReader
11854 * @param {Object} meta Metadata configuration options.
11855 * @param {Object} recordType Either an Array of field definition objects
11856 * as specified to {@link Roo.data.Record#create},
11857 * or an {@link Roo.data.Record} object
11858 * created using {@link Roo.data.Record#create}.
11860 Roo.data.ArrayReader = function(meta, recordType){
11861 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11864 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11866 * Create a data block containing Roo.data.Records from an XML document.
11867 * @param {Object} o An Array of row objects which represents the dataset.
11868 * @return {Object} data A data block which is used by an Roo.data.Store object as
11869 * a cache of Roo.data.Records.
11871 readRecords : function(o){
11872 var sid = this.meta ? this.meta.id : null;
11873 var recordType = this.recordType, fields = recordType.prototype.fields;
11876 for(var i = 0; i < root.length; i++){
11879 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11880 for(var j = 0, jlen = fields.length; j < jlen; j++){
11881 var f = fields.items[j];
11882 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11883 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11885 values[f.name] = v;
11887 var record = new recordType(values, id);
11889 records[records.length] = record;
11893 totalRecords : records.length
11902 * @class Roo.bootstrap.ComboBox
11903 * @extends Roo.bootstrap.TriggerField
11904 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11905 * @cfg {Boolean} append (true|false) default false
11906 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11907 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11908 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11909 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11910 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11911 * @cfg {Boolean} animate default true
11912 * @cfg {Boolean} emptyResultText only for touch device
11913 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11915 * Create a new ComboBox.
11916 * @param {Object} config Configuration options
11918 Roo.bootstrap.ComboBox = function(config){
11919 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11923 * Fires when the dropdown list is expanded
11924 * @param {Roo.bootstrap.ComboBox} combo This combo box
11929 * Fires when the dropdown list is collapsed
11930 * @param {Roo.bootstrap.ComboBox} combo This combo box
11934 * @event beforeselect
11935 * Fires before a list item is selected. Return false to cancel the selection.
11936 * @param {Roo.bootstrap.ComboBox} combo This combo box
11937 * @param {Roo.data.Record} record The data record returned from the underlying store
11938 * @param {Number} index The index of the selected item in the dropdown list
11940 'beforeselect' : true,
11943 * Fires when a list item is selected
11944 * @param {Roo.bootstrap.ComboBox} combo This combo box
11945 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11946 * @param {Number} index The index of the selected item in the dropdown list
11950 * @event beforequery
11951 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11952 * The event object passed has these properties:
11953 * @param {Roo.bootstrap.ComboBox} combo This combo box
11954 * @param {String} query The query
11955 * @param {Boolean} forceAll true to force "all" query
11956 * @param {Boolean} cancel true to cancel the query
11957 * @param {Object} e The query event object
11959 'beforequery': true,
11962 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11963 * @param {Roo.bootstrap.ComboBox} combo This combo box
11968 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11969 * @param {Roo.bootstrap.ComboBox} combo This combo box
11970 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11975 * Fires when the remove value from the combobox array
11976 * @param {Roo.bootstrap.ComboBox} combo This combo box
11980 * @event afterremove
11981 * Fires when the remove value from the combobox array
11982 * @param {Roo.bootstrap.ComboBox} combo This combo box
11984 'afterremove' : true,
11986 * @event specialfilter
11987 * Fires when specialfilter
11988 * @param {Roo.bootstrap.ComboBox} combo This combo box
11990 'specialfilter' : true,
11993 * Fires when tick the element
11994 * @param {Roo.bootstrap.ComboBox} combo This combo box
11998 * @event touchviewdisplay
11999 * Fires when touch view require special display (default is using displayField)
12000 * @param {Roo.bootstrap.ComboBox} combo This combo box
12001 * @param {Object} cfg set html .
12003 'touchviewdisplay' : true
12008 this.tickItems = [];
12010 this.selectedIndex = -1;
12011 if(this.mode == 'local'){
12012 if(config.queryDelay === undefined){
12013 this.queryDelay = 10;
12015 if(config.minChars === undefined){
12021 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12024 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12025 * rendering into an Roo.Editor, defaults to false)
12028 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12029 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12032 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12035 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12036 * the dropdown list (defaults to undefined, with no header element)
12040 * @cfg {String/Roo.Template} tpl The template to use to render the output
12044 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12046 listWidth: undefined,
12048 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12049 * mode = 'remote' or 'text' if mode = 'local')
12051 displayField: undefined,
12054 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12055 * mode = 'remote' or 'value' if mode = 'local').
12056 * Note: use of a valueField requires the user make a selection
12057 * in order for a value to be mapped.
12059 valueField: undefined,
12061 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12066 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12067 * field's data value (defaults to the underlying DOM element's name)
12069 hiddenName: undefined,
12071 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12075 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12077 selectedClass: 'active',
12080 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12084 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12085 * anchor positions (defaults to 'tl-bl')
12087 listAlign: 'tl-bl?',
12089 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12093 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12094 * query specified by the allQuery config option (defaults to 'query')
12096 triggerAction: 'query',
12098 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12099 * (defaults to 4, does not apply if editable = false)
12103 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12104 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12108 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12109 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12113 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12114 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12118 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12119 * when editable = true (defaults to false)
12121 selectOnFocus:false,
12123 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12125 queryParam: 'query',
12127 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12128 * when mode = 'remote' (defaults to 'Loading...')
12130 loadingText: 'Loading...',
12132 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12136 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12140 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12141 * traditional select (defaults to true)
12145 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12149 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12153 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12154 * listWidth has a higher value)
12158 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12159 * allow the user to set arbitrary text into the field (defaults to false)
12161 forceSelection:false,
12163 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12164 * if typeAhead = true (defaults to 250)
12166 typeAheadDelay : 250,
12168 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12169 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12171 valueNotFoundText : undefined,
12173 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12175 blockFocus : false,
12178 * @cfg {Boolean} disableClear Disable showing of clear button.
12180 disableClear : false,
12182 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12184 alwaysQuery : false,
12187 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12192 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12194 invalidClass : "has-warning",
12197 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12199 validClass : "has-success",
12202 * @cfg {Boolean} specialFilter (true|false) special filter default false
12204 specialFilter : false,
12207 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12209 mobileTouchView : true,
12221 btnPosition : 'right',
12222 triggerList : true,
12223 showToggleBtn : true,
12225 emptyResultText: 'Empty',
12226 triggerText : 'Select',
12228 // element that contains real text value.. (when hidden is used..)
12230 getAutoCreate : function()
12238 if(Roo.isTouch && this.mobileTouchView){
12239 cfg = this.getAutoCreateTouchView();
12246 if(!this.tickable){
12247 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12252 * ComboBox with tickable selections
12255 var align = this.labelAlign || this.parentLabelAlign();
12258 cls : 'form-group roo-combobox-tickable' //input-group
12263 cls : 'tickable-buttons',
12268 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12269 html : this.triggerText
12275 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12282 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12289 buttons.cn.unshift({
12291 cls: 'roo-select2-search-field-input'
12297 Roo.each(buttons.cn, function(c){
12299 c.cls += ' btn-' + _this.size;
12302 if (_this.disabled) {
12313 cls: 'form-hidden-field'
12317 cls: 'roo-select2-choices',
12321 cls: 'roo-select2-search-field',
12333 cls: 'roo-select2-container input-group roo-select2-container-multi',
12338 // cls: 'typeahead typeahead-long dropdown-menu',
12339 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12344 if(this.hasFeedback && !this.allowBlank){
12348 cls: 'glyphicon form-control-feedback'
12351 combobox.cn.push(feedback);
12354 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12356 // Roo.log("left and has label");
12360 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12361 tooltip : 'This field is required'
12366 cls : 'control-label col-sm-' + this.labelWidth,
12367 html : this.fieldLabel
12371 cls : "col-sm-" + (12 - this.labelWidth),
12379 if(this.indicatorpos == 'right'){
12385 cls : 'control-label col-sm-' + this.labelWidth,
12386 html : this.fieldLabel
12391 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12392 tooltip : 'This field is required'
12395 cls : "col-sm-" + (12 - this.labelWidth),
12406 } else if ( this.fieldLabel.length) {
12407 // Roo.log(" label");
12411 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12412 tooltip : 'This field is required'
12416 //cls : 'input-group-addon',
12417 html : this.fieldLabel
12425 if(this.indicatorpos == 'right'){
12430 //cls : 'input-group-addon',
12431 html : this.fieldLabel
12437 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12438 tooltip : 'This field is required'
12449 // Roo.log(" no label && no align");
12456 ['xs','sm','md','lg'].map(function(size){
12457 if (settings[size]) {
12458 cfg.cls += ' col-' + size + '-' + settings[size];
12466 _initEventsCalled : false,
12469 initEvents: function()
12472 if (this._initEventsCalled) { // as we call render... prevent looping...
12475 this._initEventsCalled = true;
12478 throw "can not find store for combo";
12481 this.store = Roo.factory(this.store, Roo.data);
12483 // if we are building from html. then this element is so complex, that we can not really
12484 // use the rendered HTML.
12485 // so we have to trash and replace the previous code.
12486 if (Roo.XComponent.build_from_html) {
12488 // remove this element....
12489 var e = this.el.dom, k=0;
12490 while (e ) { e = e.previousSibling; ++k;}
12495 this.rendered = false;
12497 this.render(this.parent().getChildContainer(true), k);
12508 if(Roo.isTouch && this.mobileTouchView){
12509 this.initTouchView();
12514 this.initTickableEvents();
12518 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12520 if(this.hiddenName){
12522 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12524 this.hiddenField.dom.value =
12525 this.hiddenValue !== undefined ? this.hiddenValue :
12526 this.value !== undefined ? this.value : '';
12528 // prevent input submission
12529 this.el.dom.removeAttribute('name');
12530 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12535 // this.el.dom.setAttribute('autocomplete', 'off');
12538 var cls = 'x-combo-list';
12540 //this.list = new Roo.Layer({
12541 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12547 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12548 _this.list.setWidth(lw);
12551 this.list.on('mouseover', this.onViewOver, this);
12552 this.list.on('mousemove', this.onViewMove, this);
12554 this.list.on('scroll', this.onViewScroll, this);
12557 this.list.swallowEvent('mousewheel');
12558 this.assetHeight = 0;
12561 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12562 this.assetHeight += this.header.getHeight();
12565 this.innerList = this.list.createChild({cls:cls+'-inner'});
12566 this.innerList.on('mouseover', this.onViewOver, this);
12567 this.innerList.on('mousemove', this.onViewMove, this);
12568 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12570 if(this.allowBlank && !this.pageSize && !this.disableClear){
12571 this.footer = this.list.createChild({cls:cls+'-ft'});
12572 this.pageTb = new Roo.Toolbar(this.footer);
12576 this.footer = this.list.createChild({cls:cls+'-ft'});
12577 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12578 {pageSize: this.pageSize});
12582 if (this.pageTb && this.allowBlank && !this.disableClear) {
12584 this.pageTb.add(new Roo.Toolbar.Fill(), {
12585 cls: 'x-btn-icon x-btn-clear',
12587 handler: function()
12590 _this.clearValue();
12591 _this.onSelect(false, -1);
12596 this.assetHeight += this.footer.getHeight();
12601 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12604 this.view = new Roo.View(this.list, this.tpl, {
12605 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12607 //this.view.wrapEl.setDisplayed(false);
12608 this.view.on('click', this.onViewClick, this);
12612 this.store.on('beforeload', this.onBeforeLoad, this);
12613 this.store.on('load', this.onLoad, this);
12614 this.store.on('loadexception', this.onLoadException, this);
12616 if(this.resizable){
12617 this.resizer = new Roo.Resizable(this.list, {
12618 pinned:true, handles:'se'
12620 this.resizer.on('resize', function(r, w, h){
12621 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12622 this.listWidth = w;
12623 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12624 this.restrictHeight();
12626 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12629 if(!this.editable){
12630 this.editable = true;
12631 this.setEditable(false);
12636 if (typeof(this.events.add.listeners) != 'undefined') {
12638 this.addicon = this.wrap.createChild(
12639 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12641 this.addicon.on('click', function(e) {
12642 this.fireEvent('add', this);
12645 if (typeof(this.events.edit.listeners) != 'undefined') {
12647 this.editicon = this.wrap.createChild(
12648 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12649 if (this.addicon) {
12650 this.editicon.setStyle('margin-left', '40px');
12652 this.editicon.on('click', function(e) {
12654 // we fire even if inothing is selected..
12655 this.fireEvent('edit', this, this.lastData );
12661 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12662 "up" : function(e){
12663 this.inKeyMode = true;
12667 "down" : function(e){
12668 if(!this.isExpanded()){
12669 this.onTriggerClick();
12671 this.inKeyMode = true;
12676 "enter" : function(e){
12677 // this.onViewClick();
12681 if(this.fireEvent("specialkey", this, e)){
12682 this.onViewClick(false);
12688 "esc" : function(e){
12692 "tab" : function(e){
12695 if(this.fireEvent("specialkey", this, e)){
12696 this.onViewClick(false);
12704 doRelay : function(foo, bar, hname){
12705 if(hname == 'down' || this.scope.isExpanded()){
12706 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12715 this.queryDelay = Math.max(this.queryDelay || 10,
12716 this.mode == 'local' ? 10 : 250);
12719 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12721 if(this.typeAhead){
12722 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12724 if(this.editable !== false){
12725 this.inputEl().on("keyup", this.onKeyUp, this);
12727 if(this.forceSelection){
12728 this.inputEl().on('blur', this.doForce, this);
12732 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12733 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12737 initTickableEvents: function()
12741 if(this.hiddenName){
12743 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12745 this.hiddenField.dom.value =
12746 this.hiddenValue !== undefined ? this.hiddenValue :
12747 this.value !== undefined ? this.value : '';
12749 // prevent input submission
12750 this.el.dom.removeAttribute('name');
12751 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12756 // this.list = this.el.select('ul.dropdown-menu',true).first();
12758 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12759 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12760 if(this.triggerList){
12761 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12764 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12765 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12767 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12768 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12770 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12771 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12773 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12774 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12775 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12778 this.cancelBtn.hide();
12783 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12784 _this.list.setWidth(lw);
12787 this.list.on('mouseover', this.onViewOver, this);
12788 this.list.on('mousemove', this.onViewMove, this);
12790 this.list.on('scroll', this.onViewScroll, this);
12793 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>';
12796 this.view = new Roo.View(this.list, this.tpl, {
12797 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12800 //this.view.wrapEl.setDisplayed(false);
12801 this.view.on('click', this.onViewClick, this);
12805 this.store.on('beforeload', this.onBeforeLoad, this);
12806 this.store.on('load', this.onLoad, this);
12807 this.store.on('loadexception', this.onLoadException, this);
12810 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12811 "up" : function(e){
12812 this.inKeyMode = true;
12816 "down" : function(e){
12817 this.inKeyMode = true;
12821 "enter" : function(e){
12822 if(this.fireEvent("specialkey", this, e)){
12823 this.onViewClick(false);
12829 "esc" : function(e){
12830 this.onTickableFooterButtonClick(e, false, false);
12833 "tab" : function(e){
12834 this.fireEvent("specialkey", this, e);
12836 this.onTickableFooterButtonClick(e, false, false);
12843 doRelay : function(e, fn, key){
12844 if(this.scope.isExpanded()){
12845 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12854 this.queryDelay = Math.max(this.queryDelay || 10,
12855 this.mode == 'local' ? 10 : 250);
12858 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12860 if(this.typeAhead){
12861 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12864 if(this.editable !== false){
12865 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12870 onDestroy : function(){
12872 this.view.setStore(null);
12873 this.view.el.removeAllListeners();
12874 this.view.el.remove();
12875 this.view.purgeListeners();
12878 this.list.dom.innerHTML = '';
12882 this.store.un('beforeload', this.onBeforeLoad, this);
12883 this.store.un('load', this.onLoad, this);
12884 this.store.un('loadexception', this.onLoadException, this);
12886 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12890 fireKey : function(e){
12891 if(e.isNavKeyPress() && !this.list.isVisible()){
12892 this.fireEvent("specialkey", this, e);
12897 onResize: function(w, h){
12898 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12900 // if(typeof w != 'number'){
12901 // // we do not handle it!?!?
12904 // var tw = this.trigger.getWidth();
12905 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12906 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12908 // this.inputEl().setWidth( this.adjustWidth('input', x));
12910 // //this.trigger.setStyle('left', x+'px');
12912 // if(this.list && this.listWidth === undefined){
12913 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12914 // this.list.setWidth(lw);
12915 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12923 * Allow or prevent the user from directly editing the field text. If false is passed,
12924 * the user will only be able to select from the items defined in the dropdown list. This method
12925 * is the runtime equivalent of setting the 'editable' config option at config time.
12926 * @param {Boolean} value True to allow the user to directly edit the field text
12928 setEditable : function(value){
12929 if(value == this.editable){
12932 this.editable = value;
12934 this.inputEl().dom.setAttribute('readOnly', true);
12935 this.inputEl().on('mousedown', this.onTriggerClick, this);
12936 this.inputEl().addClass('x-combo-noedit');
12938 this.inputEl().dom.setAttribute('readOnly', false);
12939 this.inputEl().un('mousedown', this.onTriggerClick, this);
12940 this.inputEl().removeClass('x-combo-noedit');
12946 onBeforeLoad : function(combo,opts){
12947 if(!this.hasFocus){
12951 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12953 this.restrictHeight();
12954 this.selectedIndex = -1;
12958 onLoad : function(){
12960 this.hasQuery = false;
12962 if(!this.hasFocus){
12966 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12967 this.loading.hide();
12970 if(this.store.getCount() > 0){
12972 this.restrictHeight();
12973 if(this.lastQuery == this.allQuery){
12974 if(this.editable && !this.tickable){
12975 this.inputEl().dom.select();
12979 !this.selectByValue(this.value, true) &&
12982 !this.store.lastOptions ||
12983 typeof(this.store.lastOptions.add) == 'undefined' ||
12984 this.store.lastOptions.add != true
12987 this.select(0, true);
12990 if(this.autoFocus){
12993 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12994 this.taTask.delay(this.typeAheadDelay);
12998 this.onEmptyResults();
13004 onLoadException : function()
13006 this.hasQuery = false;
13008 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13009 this.loading.hide();
13012 if(this.tickable && this.editable){
13017 // only causes errors at present
13018 //Roo.log(this.store.reader.jsonData);
13019 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13021 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13027 onTypeAhead : function(){
13028 if(this.store.getCount() > 0){
13029 var r = this.store.getAt(0);
13030 var newValue = r.data[this.displayField];
13031 var len = newValue.length;
13032 var selStart = this.getRawValue().length;
13034 if(selStart != len){
13035 this.setRawValue(newValue);
13036 this.selectText(selStart, newValue.length);
13042 onSelect : function(record, index){
13044 if(this.fireEvent('beforeselect', this, record, index) !== false){
13046 this.setFromData(index > -1 ? record.data : false);
13049 this.fireEvent('select', this, record, index);
13054 * Returns the currently selected field value or empty string if no value is set.
13055 * @return {String} value The selected value
13057 getValue : function(){
13060 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13063 if(this.valueField){
13064 return typeof this.value != 'undefined' ? this.value : '';
13066 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13071 * Clears any text/value currently set in the field
13073 clearValue : function(){
13074 if(this.hiddenField){
13075 this.hiddenField.dom.value = '';
13078 this.setRawValue('');
13079 this.lastSelectionText = '';
13080 this.lastData = false;
13082 var close = this.closeTriggerEl();
13093 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13094 * will be displayed in the field. If the value does not match the data value of an existing item,
13095 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13096 * Otherwise the field will be blank (although the value will still be set).
13097 * @param {String} value The value to match
13099 setValue : function(v){
13106 if(this.valueField){
13107 var r = this.findRecord(this.valueField, v);
13109 text = r.data[this.displayField];
13110 }else if(this.valueNotFoundText !== undefined){
13111 text = this.valueNotFoundText;
13114 this.lastSelectionText = text;
13115 if(this.hiddenField){
13116 this.hiddenField.dom.value = v;
13118 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13121 var close = this.closeTriggerEl();
13124 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13130 * @property {Object} the last set data for the element
13135 * Sets the value of the field based on a object which is related to the record format for the store.
13136 * @param {Object} value the value to set as. or false on reset?
13138 setFromData : function(o){
13145 var dv = ''; // display value
13146 var vv = ''; // value value..
13148 if (this.displayField) {
13149 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13151 // this is an error condition!!!
13152 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13155 if(this.valueField){
13156 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13159 var close = this.closeTriggerEl();
13162 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13165 if(this.hiddenField){
13166 this.hiddenField.dom.value = vv;
13168 this.lastSelectionText = dv;
13169 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13173 // no hidden field.. - we store the value in 'value', but still display
13174 // display field!!!!
13175 this.lastSelectionText = dv;
13176 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13183 reset : function(){
13184 // overridden so that last data is reset..
13191 this.setValue(this.originalValue);
13192 //this.clearInvalid();
13193 this.lastData = false;
13195 this.view.clearSelections();
13201 findRecord : function(prop, value){
13203 if(this.store.getCount() > 0){
13204 this.store.each(function(r){
13205 if(r.data[prop] == value){
13215 getName: function()
13217 // returns hidden if it's set..
13218 if (!this.rendered) {return ''};
13219 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13223 onViewMove : function(e, t){
13224 this.inKeyMode = false;
13228 onViewOver : function(e, t){
13229 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13232 var item = this.view.findItemFromChild(t);
13235 var index = this.view.indexOf(item);
13236 this.select(index, false);
13241 onViewClick : function(view, doFocus, el, e)
13243 var index = this.view.getSelectedIndexes()[0];
13245 var r = this.store.getAt(index);
13249 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13256 Roo.each(this.tickItems, function(v,k){
13258 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13260 _this.tickItems.splice(k, 1);
13262 if(typeof(e) == 'undefined' && view == false){
13263 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13275 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13276 this.tickItems.push(r.data);
13279 if(typeof(e) == 'undefined' && view == false){
13280 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13287 this.onSelect(r, index);
13289 if(doFocus !== false && !this.blockFocus){
13290 this.inputEl().focus();
13295 restrictHeight : function(){
13296 //this.innerList.dom.style.height = '';
13297 //var inner = this.innerList.dom;
13298 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13299 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13300 //this.list.beginUpdate();
13301 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13302 this.list.alignTo(this.inputEl(), this.listAlign);
13303 this.list.alignTo(this.inputEl(), this.listAlign);
13304 //this.list.endUpdate();
13308 onEmptyResults : function(){
13310 if(this.tickable && this.editable){
13311 this.restrictHeight();
13319 * Returns true if the dropdown list is expanded, else false.
13321 isExpanded : function(){
13322 return this.list.isVisible();
13326 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13327 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13328 * @param {String} value The data value of the item to select
13329 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13330 * selected item if it is not currently in view (defaults to true)
13331 * @return {Boolean} True if the value matched an item in the list, else false
13333 selectByValue : function(v, scrollIntoView){
13334 if(v !== undefined && v !== null){
13335 var r = this.findRecord(this.valueField || this.displayField, v);
13337 this.select(this.store.indexOf(r), scrollIntoView);
13345 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13346 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13347 * @param {Number} index The zero-based index of the list item to select
13348 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13349 * selected item if it is not currently in view (defaults to true)
13351 select : function(index, scrollIntoView){
13352 this.selectedIndex = index;
13353 this.view.select(index);
13354 if(scrollIntoView !== false){
13355 var el = this.view.getNode(index);
13357 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13360 this.list.scrollChildIntoView(el, false);
13366 selectNext : function(){
13367 var ct = this.store.getCount();
13369 if(this.selectedIndex == -1){
13371 }else if(this.selectedIndex < ct-1){
13372 this.select(this.selectedIndex+1);
13378 selectPrev : function(){
13379 var ct = this.store.getCount();
13381 if(this.selectedIndex == -1){
13383 }else if(this.selectedIndex != 0){
13384 this.select(this.selectedIndex-1);
13390 onKeyUp : function(e){
13391 if(this.editable !== false && !e.isSpecialKey()){
13392 this.lastKey = e.getKey();
13393 this.dqTask.delay(this.queryDelay);
13398 validateBlur : function(){
13399 return !this.list || !this.list.isVisible();
13403 initQuery : function(){
13405 var v = this.getRawValue();
13407 if(this.tickable && this.editable){
13408 v = this.tickableInputEl().getValue();
13415 doForce : function(){
13416 if(this.inputEl().dom.value.length > 0){
13417 this.inputEl().dom.value =
13418 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13424 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13425 * query allowing the query action to be canceled if needed.
13426 * @param {String} query The SQL query to execute
13427 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13428 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13429 * saved in the current store (defaults to false)
13431 doQuery : function(q, forceAll){
13433 if(q === undefined || q === null){
13438 forceAll: forceAll,
13442 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13447 forceAll = qe.forceAll;
13448 if(forceAll === true || (q.length >= this.minChars)){
13450 this.hasQuery = true;
13452 if(this.lastQuery != q || this.alwaysQuery){
13453 this.lastQuery = q;
13454 if(this.mode == 'local'){
13455 this.selectedIndex = -1;
13457 this.store.clearFilter();
13460 if(this.specialFilter){
13461 this.fireEvent('specialfilter', this);
13466 this.store.filter(this.displayField, q);
13469 this.store.fireEvent("datachanged", this.store);
13476 this.store.baseParams[this.queryParam] = q;
13478 var options = {params : this.getParams(q)};
13481 options.add = true;
13482 options.params.start = this.page * this.pageSize;
13485 this.store.load(options);
13488 * this code will make the page width larger, at the beginning, the list not align correctly,
13489 * we should expand the list on onLoad
13490 * so command out it
13495 this.selectedIndex = -1;
13500 this.loadNext = false;
13504 getParams : function(q){
13506 //p[this.queryParam] = q;
13510 p.limit = this.pageSize;
13516 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13518 collapse : function(){
13519 if(!this.isExpanded()){
13526 this.hasFocus = false;
13528 this.cancelBtn.hide();
13529 this.trigger.show();
13532 this.tickableInputEl().dom.value = '';
13533 this.tickableInputEl().blur();
13538 Roo.get(document).un('mousedown', this.collapseIf, this);
13539 Roo.get(document).un('mousewheel', this.collapseIf, this);
13540 if (!this.editable) {
13541 Roo.get(document).un('keydown', this.listKeyPress, this);
13543 this.fireEvent('collapse', this);
13549 collapseIf : function(e){
13550 var in_combo = e.within(this.el);
13551 var in_list = e.within(this.list);
13552 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13554 if (in_combo || in_list || is_list) {
13555 //e.stopPropagation();
13560 this.onTickableFooterButtonClick(e, false, false);
13568 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13570 expand : function(){
13572 if(this.isExpanded() || !this.hasFocus){
13576 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13577 this.list.setWidth(lw);
13584 this.restrictHeight();
13588 this.tickItems = Roo.apply([], this.item);
13591 this.cancelBtn.show();
13592 this.trigger.hide();
13595 this.tickableInputEl().focus();
13600 Roo.get(document).on('mousedown', this.collapseIf, this);
13601 Roo.get(document).on('mousewheel', this.collapseIf, this);
13602 if (!this.editable) {
13603 Roo.get(document).on('keydown', this.listKeyPress, this);
13606 this.fireEvent('expand', this);
13610 // Implements the default empty TriggerField.onTriggerClick function
13611 onTriggerClick : function(e)
13613 Roo.log('trigger click');
13615 if(this.disabled || !this.triggerList){
13620 this.loadNext = false;
13622 if(this.isExpanded()){
13624 if (!this.blockFocus) {
13625 this.inputEl().focus();
13629 this.hasFocus = true;
13630 if(this.triggerAction == 'all') {
13631 this.doQuery(this.allQuery, true);
13633 this.doQuery(this.getRawValue());
13635 if (!this.blockFocus) {
13636 this.inputEl().focus();
13641 onTickableTriggerClick : function(e)
13648 this.loadNext = false;
13649 this.hasFocus = true;
13651 if(this.triggerAction == 'all') {
13652 this.doQuery(this.allQuery, true);
13654 this.doQuery(this.getRawValue());
13658 onSearchFieldClick : function(e)
13660 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13661 this.onTickableFooterButtonClick(e, false, false);
13665 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13670 this.loadNext = false;
13671 this.hasFocus = true;
13673 if(this.triggerAction == 'all') {
13674 this.doQuery(this.allQuery, true);
13676 this.doQuery(this.getRawValue());
13680 listKeyPress : function(e)
13682 //Roo.log('listkeypress');
13683 // scroll to first matching element based on key pres..
13684 if (e.isSpecialKey()) {
13687 var k = String.fromCharCode(e.getKey()).toUpperCase();
13690 var csel = this.view.getSelectedNodes();
13691 var cselitem = false;
13693 var ix = this.view.indexOf(csel[0]);
13694 cselitem = this.store.getAt(ix);
13695 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13701 this.store.each(function(v) {
13703 // start at existing selection.
13704 if (cselitem.id == v.id) {
13710 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13711 match = this.store.indexOf(v);
13717 if (match === false) {
13718 return true; // no more action?
13721 this.view.select(match);
13722 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13723 sn.scrollIntoView(sn.dom.parentNode, false);
13726 onViewScroll : function(e, t){
13728 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){
13732 this.hasQuery = true;
13734 this.loading = this.list.select('.loading', true).first();
13736 if(this.loading === null){
13737 this.list.createChild({
13739 cls: 'loading roo-select2-more-results roo-select2-active',
13740 html: 'Loading more results...'
13743 this.loading = this.list.select('.loading', true).first();
13745 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13747 this.loading.hide();
13750 this.loading.show();
13755 this.loadNext = true;
13757 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13762 addItem : function(o)
13764 var dv = ''; // display value
13766 if (this.displayField) {
13767 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13769 // this is an error condition!!!
13770 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13777 var choice = this.choices.createChild({
13779 cls: 'roo-select2-search-choice',
13788 cls: 'roo-select2-search-choice-close',
13793 }, this.searchField);
13795 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13797 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13805 this.inputEl().dom.value = '';
13810 onRemoveItem : function(e, _self, o)
13812 e.preventDefault();
13814 this.lastItem = Roo.apply([], this.item);
13816 var index = this.item.indexOf(o.data) * 1;
13819 Roo.log('not this item?!');
13823 this.item.splice(index, 1);
13828 this.fireEvent('remove', this, e);
13834 syncValue : function()
13836 if(!this.item.length){
13843 Roo.each(this.item, function(i){
13844 if(_this.valueField){
13845 value.push(i[_this.valueField]);
13852 this.value = value.join(',');
13854 if(this.hiddenField){
13855 this.hiddenField.dom.value = this.value;
13858 this.store.fireEvent("datachanged", this.store);
13863 clearItem : function()
13865 if(!this.multiple){
13871 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13879 if(this.tickable && !Roo.isTouch){
13880 this.view.refresh();
13884 inputEl: function ()
13886 if(Roo.isTouch && this.mobileTouchView){
13887 return this.el.select('input.form-control',true).first();
13891 return this.searchField;
13894 return this.el.select('input.form-control',true).first();
13898 onTickableFooterButtonClick : function(e, btn, el)
13900 e.preventDefault();
13902 this.lastItem = Roo.apply([], this.item);
13904 if(btn && btn.name == 'cancel'){
13905 this.tickItems = Roo.apply([], this.item);
13914 Roo.each(this.tickItems, function(o){
13922 validate : function()
13924 var v = this.getRawValue();
13927 v = this.getValue();
13930 if(this.disabled || this.allowBlank || v.length){
13935 this.markInvalid();
13939 tickableInputEl : function()
13941 if(!this.tickable || !this.editable){
13942 return this.inputEl();
13945 return this.inputEl().select('.roo-select2-search-field-input', true).first();
13949 getAutoCreateTouchView : function()
13954 cls: 'form-group' //input-group
13960 type : this.inputType,
13961 cls : 'form-control x-combo-noedit',
13962 autocomplete: 'new-password',
13963 placeholder : this.placeholder || '',
13968 input.name = this.name;
13972 input.cls += ' input-' + this.size;
13975 if (this.disabled) {
13976 input.disabled = true;
13987 inputblock.cls += ' input-group';
13989 inputblock.cn.unshift({
13991 cls : 'input-group-addon',
13996 if(this.removable && !this.multiple){
13997 inputblock.cls += ' roo-removable';
13999 inputblock.cn.push({
14002 cls : 'roo-combo-removable-btn close'
14006 if(this.hasFeedback && !this.allowBlank){
14008 inputblock.cls += ' has-feedback';
14010 inputblock.cn.push({
14012 cls: 'glyphicon form-control-feedback'
14019 inputblock.cls += (this.before) ? '' : ' input-group';
14021 inputblock.cn.push({
14023 cls : 'input-group-addon',
14034 cls: 'form-hidden-field'
14048 cls: 'form-hidden-field'
14052 cls: 'roo-select2-choices',
14056 cls: 'roo-select2-search-field',
14069 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14075 if(!this.multiple && this.showToggleBtn){
14082 if (this.caret != false) {
14085 cls: 'fa fa-' + this.caret
14092 cls : 'input-group-addon btn dropdown-toggle',
14097 cls: 'combobox-clear',
14111 combobox.cls += ' roo-select2-container-multi';
14114 var align = this.labelAlign || this.parentLabelAlign();
14118 if(this.fieldLabel.length && this.labelWidth){
14120 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14121 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14126 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14127 tooltip : 'This field is required'
14131 cls : 'control-label ' + lw,
14132 html : this.fieldLabel
14143 if(this.indicatorpos == 'right'){
14147 cls : 'control-label ' + lw,
14148 html : this.fieldLabel
14153 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14154 tooltip : 'This field is required'
14166 var settings = this;
14168 ['xs','sm','md','lg'].map(function(size){
14169 if (settings[size]) {
14170 cfg.cls += ' col-' + size + '-' + settings[size];
14177 initTouchView : function()
14179 this.renderTouchView();
14181 this.touchViewEl.on('scroll', function(){
14182 this.el.dom.scrollTop = 0;
14185 this.originalValue = this.getValue();
14187 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14189 this.inputEl().on("click", this.showTouchView, this);
14190 this.triggerEl.on("click", this.showTouchView, this);
14192 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14193 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14195 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14197 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14198 this.store.on('load', this.onTouchViewLoad, this);
14199 this.store.on('loadexception', this.onTouchViewLoadException, this);
14201 if(this.hiddenName){
14203 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14205 this.hiddenField.dom.value =
14206 this.hiddenValue !== undefined ? this.hiddenValue :
14207 this.value !== undefined ? this.value : '';
14209 this.el.dom.removeAttribute('name');
14210 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14214 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14215 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14218 if(this.removable && !this.multiple){
14219 var close = this.closeTriggerEl();
14221 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14222 close.on('click', this.removeBtnClick, this, close);
14226 * fix the bug in Safari iOS8
14228 this.inputEl().on("focus", function(e){
14229 document.activeElement.blur();
14237 renderTouchView : function()
14239 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14240 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14242 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14243 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14245 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14246 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14247 this.touchViewBodyEl.setStyle('overflow', 'auto');
14249 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14250 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14252 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14253 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14257 showTouchView : function()
14263 this.touchViewHeaderEl.hide();
14265 if(this.modalTitle.length){
14266 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14267 this.touchViewHeaderEl.show();
14270 this.touchViewEl.show();
14272 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14273 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14274 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14276 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14278 if(this.modalTitle.length){
14279 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14282 this.touchViewBodyEl.setHeight(bodyHeight);
14286 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14288 this.touchViewEl.addClass('in');
14291 this.doTouchViewQuery();
14295 hideTouchView : function()
14297 this.touchViewEl.removeClass('in');
14301 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14303 this.touchViewEl.setStyle('display', 'none');
14308 setTouchViewValue : function()
14315 Roo.each(this.tickItems, function(o){
14320 this.hideTouchView();
14323 doTouchViewQuery : function()
14332 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14336 if(!this.alwaysQuery || this.mode == 'local'){
14337 this.onTouchViewLoad();
14344 onTouchViewBeforeLoad : function(combo,opts)
14350 onTouchViewLoad : function()
14352 if(this.store.getCount() < 1){
14353 this.onTouchViewEmptyResults();
14357 this.clearTouchView();
14359 var rawValue = this.getRawValue();
14361 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14363 this.tickItems = [];
14365 this.store.data.each(function(d, rowIndex){
14366 var row = this.touchViewListGroup.createChild(template);
14368 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14369 row.addClass(d.data.cls);
14372 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14375 html : d.data[this.displayField]
14378 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14379 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14382 row.removeClass('selected');
14383 if(!this.multiple && this.valueField &&
14384 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14387 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14388 row.addClass('selected');
14391 if(this.multiple && this.valueField &&
14392 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14396 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14397 this.tickItems.push(d.data);
14400 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14404 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14406 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14408 if(this.modalTitle.length){
14409 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14412 var listHeight = this.touchViewListGroup.getHeight();
14416 if(firstChecked && listHeight > bodyHeight){
14417 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14422 onTouchViewLoadException : function()
14424 this.hideTouchView();
14427 onTouchViewEmptyResults : function()
14429 this.clearTouchView();
14431 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14433 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14437 clearTouchView : function()
14439 this.touchViewListGroup.dom.innerHTML = '';
14442 onTouchViewClick : function(e, el, o)
14444 e.preventDefault();
14447 var rowIndex = o.rowIndex;
14449 var r = this.store.getAt(rowIndex);
14451 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14453 if(!this.multiple){
14454 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14455 c.dom.removeAttribute('checked');
14458 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14460 this.setFromData(r.data);
14462 var close = this.closeTriggerEl();
14468 this.hideTouchView();
14470 this.fireEvent('select', this, r, rowIndex);
14475 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14476 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14477 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14481 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14482 this.addItem(r.data);
14483 this.tickItems.push(r.data);
14489 * @cfg {Boolean} grow
14493 * @cfg {Number} growMin
14497 * @cfg {Number} growMax
14506 Roo.apply(Roo.bootstrap.ComboBox, {
14510 cls: 'modal-header',
14532 cls: 'list-group-item',
14536 cls: 'roo-combobox-list-group-item-value'
14540 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14554 listItemCheckbox : {
14556 cls: 'list-group-item',
14560 cls: 'roo-combobox-list-group-item-value'
14564 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14580 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14585 cls: 'modal-footer',
14593 cls: 'col-xs-6 text-left',
14596 cls: 'btn btn-danger roo-touch-view-cancel',
14602 cls: 'col-xs-6 text-right',
14605 cls: 'btn btn-success roo-touch-view-ok',
14616 Roo.apply(Roo.bootstrap.ComboBox, {
14618 touchViewTemplate : {
14620 cls: 'modal fade roo-combobox-touch-view',
14624 cls: 'modal-dialog',
14625 style : 'position:fixed', // we have to fix position....
14629 cls: 'modal-content',
14631 Roo.bootstrap.ComboBox.header,
14632 Roo.bootstrap.ComboBox.body,
14633 Roo.bootstrap.ComboBox.footer
14642 * Ext JS Library 1.1.1
14643 * Copyright(c) 2006-2007, Ext JS, LLC.
14645 * Originally Released Under LGPL - original licence link has changed is not relivant.
14648 * <script type="text/javascript">
14653 * @extends Roo.util.Observable
14654 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14655 * This class also supports single and multi selection modes. <br>
14656 * Create a data model bound view:
14658 var store = new Roo.data.Store(...);
14660 var view = new Roo.View({
14662 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14664 singleSelect: true,
14665 selectedClass: "ydataview-selected",
14669 // listen for node click?
14670 view.on("click", function(vw, index, node, e){
14671 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14675 dataModel.load("foobar.xml");
14677 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14679 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14680 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14682 * Note: old style constructor is still suported (container, template, config)
14685 * Create a new View
14686 * @param {Object} config The config object
14689 Roo.View = function(config, depreciated_tpl, depreciated_config){
14691 this.parent = false;
14693 if (typeof(depreciated_tpl) == 'undefined') {
14694 // new way.. - universal constructor.
14695 Roo.apply(this, config);
14696 this.el = Roo.get(this.el);
14699 this.el = Roo.get(config);
14700 this.tpl = depreciated_tpl;
14701 Roo.apply(this, depreciated_config);
14703 this.wrapEl = this.el.wrap().wrap();
14704 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14707 if(typeof(this.tpl) == "string"){
14708 this.tpl = new Roo.Template(this.tpl);
14710 // support xtype ctors..
14711 this.tpl = new Roo.factory(this.tpl, Roo);
14715 this.tpl.compile();
14720 * @event beforeclick
14721 * Fires before a click is processed. Returns false to cancel the default action.
14722 * @param {Roo.View} this
14723 * @param {Number} index The index of the target node
14724 * @param {HTMLElement} node The target node
14725 * @param {Roo.EventObject} e The raw event object
14727 "beforeclick" : true,
14730 * Fires when a template node is clicked.
14731 * @param {Roo.View} this
14732 * @param {Number} index The index of the target node
14733 * @param {HTMLElement} node The target node
14734 * @param {Roo.EventObject} e The raw event object
14739 * Fires when a template node is double clicked.
14740 * @param {Roo.View} this
14741 * @param {Number} index The index of the target node
14742 * @param {HTMLElement} node The target node
14743 * @param {Roo.EventObject} e The raw event object
14747 * @event contextmenu
14748 * Fires when a template node is right clicked.
14749 * @param {Roo.View} this
14750 * @param {Number} index The index of the target node
14751 * @param {HTMLElement} node The target node
14752 * @param {Roo.EventObject} e The raw event object
14754 "contextmenu" : true,
14756 * @event selectionchange
14757 * Fires when the selected nodes change.
14758 * @param {Roo.View} this
14759 * @param {Array} selections Array of the selected nodes
14761 "selectionchange" : true,
14764 * @event beforeselect
14765 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14766 * @param {Roo.View} this
14767 * @param {HTMLElement} node The node to be selected
14768 * @param {Array} selections Array of currently selected nodes
14770 "beforeselect" : true,
14772 * @event preparedata
14773 * Fires on every row to render, to allow you to change the data.
14774 * @param {Roo.View} this
14775 * @param {Object} data to be rendered (change this)
14777 "preparedata" : true
14785 "click": this.onClick,
14786 "dblclick": this.onDblClick,
14787 "contextmenu": this.onContextMenu,
14791 this.selections = [];
14793 this.cmp = new Roo.CompositeElementLite([]);
14795 this.store = Roo.factory(this.store, Roo.data);
14796 this.setStore(this.store, true);
14799 if ( this.footer && this.footer.xtype) {
14801 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14803 this.footer.dataSource = this.store;
14804 this.footer.container = fctr;
14805 this.footer = Roo.factory(this.footer, Roo);
14806 fctr.insertFirst(this.el);
14808 // this is a bit insane - as the paging toolbar seems to detach the el..
14809 // dom.parentNode.parentNode.parentNode
14810 // they get detached?
14814 Roo.View.superclass.constructor.call(this);
14819 Roo.extend(Roo.View, Roo.util.Observable, {
14822 * @cfg {Roo.data.Store} store Data store to load data from.
14827 * @cfg {String|Roo.Element} el The container element.
14832 * @cfg {String|Roo.Template} tpl The template used by this View
14836 * @cfg {String} dataName the named area of the template to use as the data area
14837 * Works with domtemplates roo-name="name"
14841 * @cfg {String} selectedClass The css class to add to selected nodes
14843 selectedClass : "x-view-selected",
14845 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14850 * @cfg {String} text to display on mask (default Loading)
14854 * @cfg {Boolean} multiSelect Allow multiple selection
14856 multiSelect : false,
14858 * @cfg {Boolean} singleSelect Allow single selection
14860 singleSelect: false,
14863 * @cfg {Boolean} toggleSelect - selecting
14865 toggleSelect : false,
14868 * @cfg {Boolean} tickable - selecting
14873 * Returns the element this view is bound to.
14874 * @return {Roo.Element}
14876 getEl : function(){
14877 return this.wrapEl;
14883 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14885 refresh : function(){
14886 //Roo.log('refresh');
14889 // if we are using something like 'domtemplate', then
14890 // the what gets used is:
14891 // t.applySubtemplate(NAME, data, wrapping data..)
14892 // the outer template then get' applied with
14893 // the store 'extra data'
14894 // and the body get's added to the
14895 // roo-name="data" node?
14896 // <span class='roo-tpl-{name}'></span> ?????
14900 this.clearSelections();
14901 this.el.update("");
14903 var records = this.store.getRange();
14904 if(records.length < 1) {
14906 // is this valid?? = should it render a template??
14908 this.el.update(this.emptyText);
14912 if (this.dataName) {
14913 this.el.update(t.apply(this.store.meta)); //????
14914 el = this.el.child('.roo-tpl-' + this.dataName);
14917 for(var i = 0, len = records.length; i < len; i++){
14918 var data = this.prepareData(records[i].data, i, records[i]);
14919 this.fireEvent("preparedata", this, data, i, records[i]);
14921 var d = Roo.apply({}, data);
14924 Roo.apply(d, {'roo-id' : Roo.id()});
14928 Roo.each(this.parent.item, function(item){
14929 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14932 Roo.apply(d, {'roo-data-checked' : 'checked'});
14936 html[html.length] = Roo.util.Format.trim(
14938 t.applySubtemplate(this.dataName, d, this.store.meta) :
14945 el.update(html.join(""));
14946 this.nodes = el.dom.childNodes;
14947 this.updateIndexes(0);
14952 * Function to override to reformat the data that is sent to
14953 * the template for each node.
14954 * DEPRICATED - use the preparedata event handler.
14955 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14956 * a JSON object for an UpdateManager bound view).
14958 prepareData : function(data, index, record)
14960 this.fireEvent("preparedata", this, data, index, record);
14964 onUpdate : function(ds, record){
14965 // Roo.log('on update');
14966 this.clearSelections();
14967 var index = this.store.indexOf(record);
14968 var n = this.nodes[index];
14969 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14970 n.parentNode.removeChild(n);
14971 this.updateIndexes(index, index);
14977 onAdd : function(ds, records, index)
14979 //Roo.log(['on Add', ds, records, index] );
14980 this.clearSelections();
14981 if(this.nodes.length == 0){
14985 var n = this.nodes[index];
14986 for(var i = 0, len = records.length; i < len; i++){
14987 var d = this.prepareData(records[i].data, i, records[i]);
14989 this.tpl.insertBefore(n, d);
14992 this.tpl.append(this.el, d);
14995 this.updateIndexes(index);
14998 onRemove : function(ds, record, index){
14999 // Roo.log('onRemove');
15000 this.clearSelections();
15001 var el = this.dataName ?
15002 this.el.child('.roo-tpl-' + this.dataName) :
15005 el.dom.removeChild(this.nodes[index]);
15006 this.updateIndexes(index);
15010 * Refresh an individual node.
15011 * @param {Number} index
15013 refreshNode : function(index){
15014 this.onUpdate(this.store, this.store.getAt(index));
15017 updateIndexes : function(startIndex, endIndex){
15018 var ns = this.nodes;
15019 startIndex = startIndex || 0;
15020 endIndex = endIndex || ns.length - 1;
15021 for(var i = startIndex; i <= endIndex; i++){
15022 ns[i].nodeIndex = i;
15027 * Changes the data store this view uses and refresh the view.
15028 * @param {Store} store
15030 setStore : function(store, initial){
15031 if(!initial && this.store){
15032 this.store.un("datachanged", this.refresh);
15033 this.store.un("add", this.onAdd);
15034 this.store.un("remove", this.onRemove);
15035 this.store.un("update", this.onUpdate);
15036 this.store.un("clear", this.refresh);
15037 this.store.un("beforeload", this.onBeforeLoad);
15038 this.store.un("load", this.onLoad);
15039 this.store.un("loadexception", this.onLoad);
15043 store.on("datachanged", this.refresh, this);
15044 store.on("add", this.onAdd, this);
15045 store.on("remove", this.onRemove, this);
15046 store.on("update", this.onUpdate, this);
15047 store.on("clear", this.refresh, this);
15048 store.on("beforeload", this.onBeforeLoad, this);
15049 store.on("load", this.onLoad, this);
15050 store.on("loadexception", this.onLoad, this);
15058 * onbeforeLoad - masks the loading area.
15061 onBeforeLoad : function(store,opts)
15063 //Roo.log('onBeforeLoad');
15065 this.el.update("");
15067 this.el.mask(this.mask ? this.mask : "Loading" );
15069 onLoad : function ()
15076 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15077 * @param {HTMLElement} node
15078 * @return {HTMLElement} The template node
15080 findItemFromChild : function(node){
15081 var el = this.dataName ?
15082 this.el.child('.roo-tpl-' + this.dataName,true) :
15085 if(!node || node.parentNode == el){
15088 var p = node.parentNode;
15089 while(p && p != el){
15090 if(p.parentNode == el){
15099 onClick : function(e){
15100 var item = this.findItemFromChild(e.getTarget());
15102 var index = this.indexOf(item);
15103 if(this.onItemClick(item, index, e) !== false){
15104 this.fireEvent("click", this, index, item, e);
15107 this.clearSelections();
15112 onContextMenu : function(e){
15113 var item = this.findItemFromChild(e.getTarget());
15115 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15120 onDblClick : function(e){
15121 var item = this.findItemFromChild(e.getTarget());
15123 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15127 onItemClick : function(item, index, e)
15129 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15132 if (this.toggleSelect) {
15133 var m = this.isSelected(item) ? 'unselect' : 'select';
15136 _t[m](item, true, false);
15139 if(this.multiSelect || this.singleSelect){
15140 if(this.multiSelect && e.shiftKey && this.lastSelection){
15141 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15143 this.select(item, this.multiSelect && e.ctrlKey);
15144 this.lastSelection = item;
15147 if(!this.tickable){
15148 e.preventDefault();
15156 * Get the number of selected nodes.
15159 getSelectionCount : function(){
15160 return this.selections.length;
15164 * Get the currently selected nodes.
15165 * @return {Array} An array of HTMLElements
15167 getSelectedNodes : function(){
15168 return this.selections;
15172 * Get the indexes of the selected nodes.
15175 getSelectedIndexes : function(){
15176 var indexes = [], s = this.selections;
15177 for(var i = 0, len = s.length; i < len; i++){
15178 indexes.push(s[i].nodeIndex);
15184 * Clear all selections
15185 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15187 clearSelections : function(suppressEvent){
15188 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15189 this.cmp.elements = this.selections;
15190 this.cmp.removeClass(this.selectedClass);
15191 this.selections = [];
15192 if(!suppressEvent){
15193 this.fireEvent("selectionchange", this, this.selections);
15199 * Returns true if the passed node is selected
15200 * @param {HTMLElement/Number} node The node or node index
15201 * @return {Boolean}
15203 isSelected : function(node){
15204 var s = this.selections;
15208 node = this.getNode(node);
15209 return s.indexOf(node) !== -1;
15214 * @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
15215 * @param {Boolean} keepExisting (optional) true to keep existing selections
15216 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15218 select : function(nodeInfo, keepExisting, suppressEvent){
15219 if(nodeInfo instanceof Array){
15221 this.clearSelections(true);
15223 for(var i = 0, len = nodeInfo.length; i < len; i++){
15224 this.select(nodeInfo[i], true, true);
15228 var node = this.getNode(nodeInfo);
15229 if(!node || this.isSelected(node)){
15230 return; // already selected.
15233 this.clearSelections(true);
15236 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15237 Roo.fly(node).addClass(this.selectedClass);
15238 this.selections.push(node);
15239 if(!suppressEvent){
15240 this.fireEvent("selectionchange", this, this.selections);
15248 * @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
15249 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15250 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15252 unselect : function(nodeInfo, keepExisting, suppressEvent)
15254 if(nodeInfo instanceof Array){
15255 Roo.each(this.selections, function(s) {
15256 this.unselect(s, nodeInfo);
15260 var node = this.getNode(nodeInfo);
15261 if(!node || !this.isSelected(node)){
15262 //Roo.log("not selected");
15263 return; // not selected.
15267 Roo.each(this.selections, function(s) {
15269 Roo.fly(node).removeClass(this.selectedClass);
15276 this.selections= ns;
15277 this.fireEvent("selectionchange", this, this.selections);
15281 * Gets a template node.
15282 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15283 * @return {HTMLElement} The node or null if it wasn't found
15285 getNode : function(nodeInfo){
15286 if(typeof nodeInfo == "string"){
15287 return document.getElementById(nodeInfo);
15288 }else if(typeof nodeInfo == "number"){
15289 return this.nodes[nodeInfo];
15295 * Gets a range template nodes.
15296 * @param {Number} startIndex
15297 * @param {Number} endIndex
15298 * @return {Array} An array of nodes
15300 getNodes : function(start, end){
15301 var ns = this.nodes;
15302 start = start || 0;
15303 end = typeof end == "undefined" ? ns.length - 1 : end;
15306 for(var i = start; i <= end; i++){
15310 for(var i = start; i >= end; i--){
15318 * Finds the index of the passed node
15319 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15320 * @return {Number} The index of the node or -1
15322 indexOf : function(node){
15323 node = this.getNode(node);
15324 if(typeof node.nodeIndex == "number"){
15325 return node.nodeIndex;
15327 var ns = this.nodes;
15328 for(var i = 0, len = ns.length; i < len; i++){
15339 * based on jquery fullcalendar
15343 Roo.bootstrap = Roo.bootstrap || {};
15345 * @class Roo.bootstrap.Calendar
15346 * @extends Roo.bootstrap.Component
15347 * Bootstrap Calendar class
15348 * @cfg {Boolean} loadMask (true|false) default false
15349 * @cfg {Object} header generate the user specific header of the calendar, default false
15352 * Create a new Container
15353 * @param {Object} config The config object
15358 Roo.bootstrap.Calendar = function(config){
15359 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15363 * Fires when a date is selected
15364 * @param {DatePicker} this
15365 * @param {Date} date The selected date
15369 * @event monthchange
15370 * Fires when the displayed month changes
15371 * @param {DatePicker} this
15372 * @param {Date} date The selected month
15374 'monthchange': true,
15376 * @event evententer
15377 * Fires when mouse over an event
15378 * @param {Calendar} this
15379 * @param {event} Event
15381 'evententer': true,
15383 * @event eventleave
15384 * Fires when the mouse leaves an
15385 * @param {Calendar} this
15388 'eventleave': true,
15390 * @event eventclick
15391 * Fires when the mouse click an
15392 * @param {Calendar} this
15401 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15404 * @cfg {Number} startDay
15405 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15413 getAutoCreate : function(){
15416 var fc_button = function(name, corner, style, content ) {
15417 return Roo.apply({},{
15419 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15421 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15424 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15435 style : 'width:100%',
15442 cls : 'fc-header-left',
15444 fc_button('prev', 'left', 'arrow', '‹' ),
15445 fc_button('next', 'right', 'arrow', '›' ),
15446 { tag: 'span', cls: 'fc-header-space' },
15447 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15455 cls : 'fc-header-center',
15459 cls: 'fc-header-title',
15462 html : 'month / year'
15470 cls : 'fc-header-right',
15472 /* fc_button('month', 'left', '', 'month' ),
15473 fc_button('week', '', '', 'week' ),
15474 fc_button('day', 'right', '', 'day' )
15486 header = this.header;
15489 var cal_heads = function() {
15491 // fixme - handle this.
15493 for (var i =0; i < Date.dayNames.length; i++) {
15494 var d = Date.dayNames[i];
15497 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15498 html : d.substring(0,3)
15502 ret[0].cls += ' fc-first';
15503 ret[6].cls += ' fc-last';
15506 var cal_cell = function(n) {
15509 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15514 cls: 'fc-day-number',
15518 cls: 'fc-day-content',
15522 style: 'position: relative;' // height: 17px;
15534 var cal_rows = function() {
15537 for (var r = 0; r < 6; r++) {
15544 for (var i =0; i < Date.dayNames.length; i++) {
15545 var d = Date.dayNames[i];
15546 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15549 row.cn[0].cls+=' fc-first';
15550 row.cn[0].cn[0].style = 'min-height:90px';
15551 row.cn[6].cls+=' fc-last';
15555 ret[0].cls += ' fc-first';
15556 ret[4].cls += ' fc-prev-last';
15557 ret[5].cls += ' fc-last';
15564 cls: 'fc-border-separate',
15565 style : 'width:100%',
15573 cls : 'fc-first fc-last',
15591 cls : 'fc-content',
15592 style : "position: relative;",
15595 cls : 'fc-view fc-view-month fc-grid',
15596 style : 'position: relative',
15597 unselectable : 'on',
15600 cls : 'fc-event-container',
15601 style : 'position:absolute;z-index:8;top:0;left:0;'
15619 initEvents : function()
15622 throw "can not find store for calendar";
15628 style: "text-align:center",
15632 style: "background-color:white;width:50%;margin:250 auto",
15636 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15647 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15649 var size = this.el.select('.fc-content', true).first().getSize();
15650 this.maskEl.setSize(size.width, size.height);
15651 this.maskEl.enableDisplayMode("block");
15652 if(!this.loadMask){
15653 this.maskEl.hide();
15656 this.store = Roo.factory(this.store, Roo.data);
15657 this.store.on('load', this.onLoad, this);
15658 this.store.on('beforeload', this.onBeforeLoad, this);
15662 this.cells = this.el.select('.fc-day',true);
15663 //Roo.log(this.cells);
15664 this.textNodes = this.el.query('.fc-day-number');
15665 this.cells.addClassOnOver('fc-state-hover');
15667 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15668 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15669 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15670 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15672 this.on('monthchange', this.onMonthChange, this);
15674 this.update(new Date().clearTime());
15677 resize : function() {
15678 var sz = this.el.getSize();
15680 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15681 this.el.select('.fc-day-content div',true).setHeight(34);
15686 showPrevMonth : function(e){
15687 this.update(this.activeDate.add("mo", -1));
15689 showToday : function(e){
15690 this.update(new Date().clearTime());
15693 showNextMonth : function(e){
15694 this.update(this.activeDate.add("mo", 1));
15698 showPrevYear : function(){
15699 this.update(this.activeDate.add("y", -1));
15703 showNextYear : function(){
15704 this.update(this.activeDate.add("y", 1));
15709 update : function(date)
15711 var vd = this.activeDate;
15712 this.activeDate = date;
15713 // if(vd && this.el){
15714 // var t = date.getTime();
15715 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15716 // Roo.log('using add remove');
15718 // this.fireEvent('monthchange', this, date);
15720 // this.cells.removeClass("fc-state-highlight");
15721 // this.cells.each(function(c){
15722 // if(c.dateValue == t){
15723 // c.addClass("fc-state-highlight");
15724 // setTimeout(function(){
15725 // try{c.dom.firstChild.focus();}catch(e){}
15735 var days = date.getDaysInMonth();
15737 var firstOfMonth = date.getFirstDateOfMonth();
15738 var startingPos = firstOfMonth.getDay()-this.startDay;
15740 if(startingPos < this.startDay){
15744 var pm = date.add(Date.MONTH, -1);
15745 var prevStart = pm.getDaysInMonth()-startingPos;
15747 this.cells = this.el.select('.fc-day',true);
15748 this.textNodes = this.el.query('.fc-day-number');
15749 this.cells.addClassOnOver('fc-state-hover');
15751 var cells = this.cells.elements;
15752 var textEls = this.textNodes;
15754 Roo.each(cells, function(cell){
15755 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15758 days += startingPos;
15760 // convert everything to numbers so it's fast
15761 var day = 86400000;
15762 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15765 //Roo.log(prevStart);
15767 var today = new Date().clearTime().getTime();
15768 var sel = date.clearTime().getTime();
15769 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15770 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15771 var ddMatch = this.disabledDatesRE;
15772 var ddText = this.disabledDatesText;
15773 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15774 var ddaysText = this.disabledDaysText;
15775 var format = this.format;
15777 var setCellClass = function(cal, cell){
15781 //Roo.log('set Cell Class');
15783 var t = d.getTime();
15787 cell.dateValue = t;
15789 cell.className += " fc-today";
15790 cell.className += " fc-state-highlight";
15791 cell.title = cal.todayText;
15794 // disable highlight in other month..
15795 //cell.className += " fc-state-highlight";
15800 cell.className = " fc-state-disabled";
15801 cell.title = cal.minText;
15805 cell.className = " fc-state-disabled";
15806 cell.title = cal.maxText;
15810 if(ddays.indexOf(d.getDay()) != -1){
15811 cell.title = ddaysText;
15812 cell.className = " fc-state-disabled";
15815 if(ddMatch && format){
15816 var fvalue = d.dateFormat(format);
15817 if(ddMatch.test(fvalue)){
15818 cell.title = ddText.replace("%0", fvalue);
15819 cell.className = " fc-state-disabled";
15823 if (!cell.initialClassName) {
15824 cell.initialClassName = cell.dom.className;
15827 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15832 for(; i < startingPos; i++) {
15833 textEls[i].innerHTML = (++prevStart);
15834 d.setDate(d.getDate()+1);
15836 cells[i].className = "fc-past fc-other-month";
15837 setCellClass(this, cells[i]);
15842 for(; i < days; i++){
15843 intDay = i - startingPos + 1;
15844 textEls[i].innerHTML = (intDay);
15845 d.setDate(d.getDate()+1);
15847 cells[i].className = ''; // "x-date-active";
15848 setCellClass(this, cells[i]);
15852 for(; i < 42; i++) {
15853 textEls[i].innerHTML = (++extraDays);
15854 d.setDate(d.getDate()+1);
15856 cells[i].className = "fc-future fc-other-month";
15857 setCellClass(this, cells[i]);
15860 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15862 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15864 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15865 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15867 if(totalRows != 6){
15868 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15869 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15872 this.fireEvent('monthchange', this, date);
15876 if(!this.internalRender){
15877 var main = this.el.dom.firstChild;
15878 var w = main.offsetWidth;
15879 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15880 Roo.fly(main).setWidth(w);
15881 this.internalRender = true;
15882 // opera does not respect the auto grow header center column
15883 // then, after it gets a width opera refuses to recalculate
15884 // without a second pass
15885 if(Roo.isOpera && !this.secondPass){
15886 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15887 this.secondPass = true;
15888 this.update.defer(10, this, [date]);
15895 findCell : function(dt) {
15896 dt = dt.clearTime().getTime();
15898 this.cells.each(function(c){
15899 //Roo.log("check " +c.dateValue + '?=' + dt);
15900 if(c.dateValue == dt){
15910 findCells : function(ev) {
15911 var s = ev.start.clone().clearTime().getTime();
15913 var e= ev.end.clone().clearTime().getTime();
15916 this.cells.each(function(c){
15917 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15919 if(c.dateValue > e){
15922 if(c.dateValue < s){
15931 // findBestRow: function(cells)
15935 // for (var i =0 ; i < cells.length;i++) {
15936 // ret = Math.max(cells[i].rows || 0,ret);
15943 addItem : function(ev)
15945 // look for vertical location slot in
15946 var cells = this.findCells(ev);
15948 // ev.row = this.findBestRow(cells);
15950 // work out the location.
15954 for(var i =0; i < cells.length; i++) {
15956 cells[i].row = cells[0].row;
15959 cells[i].row = cells[i].row + 1;
15969 if (crow.start.getY() == cells[i].getY()) {
15971 crow.end = cells[i];
15988 cells[0].events.push(ev);
15990 this.calevents.push(ev);
15993 clearEvents: function() {
15995 if(!this.calevents){
15999 Roo.each(this.cells.elements, function(c){
16005 Roo.each(this.calevents, function(e) {
16006 Roo.each(e.els, function(el) {
16007 el.un('mouseenter' ,this.onEventEnter, this);
16008 el.un('mouseleave' ,this.onEventLeave, this);
16013 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16019 renderEvents: function()
16023 this.cells.each(function(c) {
16032 if(c.row != c.events.length){
16033 r = 4 - (4 - (c.row - c.events.length));
16036 c.events = ev.slice(0, r);
16037 c.more = ev.slice(r);
16039 if(c.more.length && c.more.length == 1){
16040 c.events.push(c.more.pop());
16043 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16047 this.cells.each(function(c) {
16049 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16052 for (var e = 0; e < c.events.length; e++){
16053 var ev = c.events[e];
16054 var rows = ev.rows;
16056 for(var i = 0; i < rows.length; i++) {
16058 // how many rows should it span..
16061 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16062 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16064 unselectable : "on",
16067 cls: 'fc-event-inner',
16071 // cls: 'fc-event-time',
16072 // html : cells.length > 1 ? '' : ev.time
16076 cls: 'fc-event-title',
16077 html : String.format('{0}', ev.title)
16084 cls: 'ui-resizable-handle ui-resizable-e',
16085 html : '  '
16092 cfg.cls += ' fc-event-start';
16094 if ((i+1) == rows.length) {
16095 cfg.cls += ' fc-event-end';
16098 var ctr = _this.el.select('.fc-event-container',true).first();
16099 var cg = ctr.createChild(cfg);
16101 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16102 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16104 var r = (c.more.length) ? 1 : 0;
16105 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16106 cg.setWidth(ebox.right - sbox.x -2);
16108 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16109 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16110 cg.on('click', _this.onEventClick, _this, ev);
16121 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16122 style : 'position: absolute',
16123 unselectable : "on",
16126 cls: 'fc-event-inner',
16130 cls: 'fc-event-title',
16138 cls: 'ui-resizable-handle ui-resizable-e',
16139 html : '  '
16145 var ctr = _this.el.select('.fc-event-container',true).first();
16146 var cg = ctr.createChild(cfg);
16148 var sbox = c.select('.fc-day-content',true).first().getBox();
16149 var ebox = c.select('.fc-day-content',true).first().getBox();
16151 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16152 cg.setWidth(ebox.right - sbox.x -2);
16154 cg.on('click', _this.onMoreEventClick, _this, c.more);
16164 onEventEnter: function (e, el,event,d) {
16165 this.fireEvent('evententer', this, el, event);
16168 onEventLeave: function (e, el,event,d) {
16169 this.fireEvent('eventleave', this, el, event);
16172 onEventClick: function (e, el,event,d) {
16173 this.fireEvent('eventclick', this, el, event);
16176 onMonthChange: function () {
16180 onMoreEventClick: function(e, el, more)
16184 this.calpopover.placement = 'right';
16185 this.calpopover.setTitle('More');
16187 this.calpopover.setContent('');
16189 var ctr = this.calpopover.el.select('.popover-content', true).first();
16191 Roo.each(more, function(m){
16193 cls : 'fc-event-hori fc-event-draggable',
16196 var cg = ctr.createChild(cfg);
16198 cg.on('click', _this.onEventClick, _this, m);
16201 this.calpopover.show(el);
16206 onLoad: function ()
16208 this.calevents = [];
16211 if(this.store.getCount() > 0){
16212 this.store.data.each(function(d){
16215 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16216 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16217 time : d.data.start_time,
16218 title : d.data.title,
16219 description : d.data.description,
16220 venue : d.data.venue
16225 this.renderEvents();
16227 if(this.calevents.length && this.loadMask){
16228 this.maskEl.hide();
16232 onBeforeLoad: function()
16234 this.clearEvents();
16236 this.maskEl.show();
16250 * @class Roo.bootstrap.Popover
16251 * @extends Roo.bootstrap.Component
16252 * Bootstrap Popover class
16253 * @cfg {String} html contents of the popover (or false to use children..)
16254 * @cfg {String} title of popover (or false to hide)
16255 * @cfg {String} placement how it is placed
16256 * @cfg {String} trigger click || hover (or false to trigger manually)
16257 * @cfg {String} over what (parent or false to trigger manually.)
16258 * @cfg {Number} delay - delay before showing
16261 * Create a new Popover
16262 * @param {Object} config The config object
16265 Roo.bootstrap.Popover = function(config){
16266 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16272 * After the popover show
16274 * @param {Roo.bootstrap.Popover} this
16279 * After the popover hide
16281 * @param {Roo.bootstrap.Popover} this
16287 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16289 title: 'Fill in a title',
16292 placement : 'right',
16293 trigger : 'hover', // hover
16299 can_build_overlaid : false,
16301 getChildContainer : function()
16303 return this.el.select('.popover-content',true).first();
16306 getAutoCreate : function(){
16309 cls : 'popover roo-dynamic',
16310 style: 'display:block',
16316 cls : 'popover-inner',
16320 cls: 'popover-title',
16324 cls : 'popover-content',
16335 setTitle: function(str)
16338 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16340 setContent: function(str)
16343 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16345 // as it get's added to the bottom of the page.
16346 onRender : function(ct, position)
16348 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16350 var cfg = Roo.apply({}, this.getAutoCreate());
16354 cfg.cls += ' ' + this.cls;
16357 cfg.style = this.style;
16359 //Roo.log("adding to ");
16360 this.el = Roo.get(document.body).createChild(cfg, position);
16361 // Roo.log(this.el);
16366 initEvents : function()
16368 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16369 this.el.enableDisplayMode('block');
16371 if (this.over === false) {
16374 if (this.triggers === false) {
16377 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16378 var triggers = this.trigger ? this.trigger.split(' ') : [];
16379 Roo.each(triggers, function(trigger) {
16381 if (trigger == 'click') {
16382 on_el.on('click', this.toggle, this);
16383 } else if (trigger != 'manual') {
16384 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16385 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16387 on_el.on(eventIn ,this.enter, this);
16388 on_el.on(eventOut, this.leave, this);
16399 toggle : function () {
16400 this.hoverState == 'in' ? this.leave() : this.enter();
16403 enter : function () {
16405 clearTimeout(this.timeout);
16407 this.hoverState = 'in';
16409 if (!this.delay || !this.delay.show) {
16414 this.timeout = setTimeout(function () {
16415 if (_t.hoverState == 'in') {
16418 }, this.delay.show)
16421 leave : function() {
16422 clearTimeout(this.timeout);
16424 this.hoverState = 'out';
16426 if (!this.delay || !this.delay.hide) {
16431 this.timeout = setTimeout(function () {
16432 if (_t.hoverState == 'out') {
16435 }, this.delay.hide)
16438 show : function (on_el)
16441 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16445 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16446 if (this.html !== false) {
16447 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16449 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16450 if (!this.title.length) {
16451 this.el.select('.popover-title',true).hide();
16454 var placement = typeof this.placement == 'function' ?
16455 this.placement.call(this, this.el, on_el) :
16458 var autoToken = /\s?auto?\s?/i;
16459 var autoPlace = autoToken.test(placement);
16461 placement = placement.replace(autoToken, '') || 'top';
16465 //this.el.setXY([0,0]);
16467 this.el.dom.style.display='block';
16468 this.el.addClass(placement);
16470 //this.el.appendTo(on_el);
16472 var p = this.getPosition();
16473 var box = this.el.getBox();
16478 var align = Roo.bootstrap.Popover.alignment[placement];
16479 this.el.alignTo(on_el, align[0],align[1]);
16480 //var arrow = this.el.select('.arrow',true).first();
16481 //arrow.set(align[2],
16483 this.el.addClass('in');
16486 if (this.el.hasClass('fade')) {
16490 this.hoverState = 'in';
16492 this.fireEvent('show', this);
16497 this.el.setXY([0,0]);
16498 this.el.removeClass('in');
16500 this.hoverState = null;
16502 this.fireEvent('hide', this);
16507 Roo.bootstrap.Popover.alignment = {
16508 'left' : ['r-l', [-10,0], 'right'],
16509 'right' : ['l-r', [10,0], 'left'],
16510 'bottom' : ['t-b', [0,10], 'top'],
16511 'top' : [ 'b-t', [0,-10], 'bottom']
16522 * @class Roo.bootstrap.Progress
16523 * @extends Roo.bootstrap.Component
16524 * Bootstrap Progress class
16525 * @cfg {Boolean} striped striped of the progress bar
16526 * @cfg {Boolean} active animated of the progress bar
16530 * Create a new Progress
16531 * @param {Object} config The config object
16534 Roo.bootstrap.Progress = function(config){
16535 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16538 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16543 getAutoCreate : function(){
16551 cfg.cls += ' progress-striped';
16555 cfg.cls += ' active';
16574 * @class Roo.bootstrap.ProgressBar
16575 * @extends Roo.bootstrap.Component
16576 * Bootstrap ProgressBar class
16577 * @cfg {Number} aria_valuenow aria-value now
16578 * @cfg {Number} aria_valuemin aria-value min
16579 * @cfg {Number} aria_valuemax aria-value max
16580 * @cfg {String} label label for the progress bar
16581 * @cfg {String} panel (success | info | warning | danger )
16582 * @cfg {String} role role of the progress bar
16583 * @cfg {String} sr_only text
16587 * Create a new ProgressBar
16588 * @param {Object} config The config object
16591 Roo.bootstrap.ProgressBar = function(config){
16592 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16595 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16599 aria_valuemax : 100,
16605 getAutoCreate : function()
16610 cls: 'progress-bar',
16611 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16623 cfg.role = this.role;
16626 if(this.aria_valuenow){
16627 cfg['aria-valuenow'] = this.aria_valuenow;
16630 if(this.aria_valuemin){
16631 cfg['aria-valuemin'] = this.aria_valuemin;
16634 if(this.aria_valuemax){
16635 cfg['aria-valuemax'] = this.aria_valuemax;
16638 if(this.label && !this.sr_only){
16639 cfg.html = this.label;
16643 cfg.cls += ' progress-bar-' + this.panel;
16649 update : function(aria_valuenow)
16651 this.aria_valuenow = aria_valuenow;
16653 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16668 * @class Roo.bootstrap.TabGroup
16669 * @extends Roo.bootstrap.Column
16670 * Bootstrap Column class
16671 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16672 * @cfg {Boolean} carousel true to make the group behave like a carousel
16673 * @cfg {Boolean} bullets show bullets for the panels
16674 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16675 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16676 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16677 * @cfg {Boolean} showarrow (true|false) show arrow default true
16680 * Create a new TabGroup
16681 * @param {Object} config The config object
16684 Roo.bootstrap.TabGroup = function(config){
16685 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16687 this.navId = Roo.id();
16690 Roo.bootstrap.TabGroup.register(this);
16694 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16697 transition : false,
16702 slideOnTouch : false,
16705 getAutoCreate : function()
16707 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16709 cfg.cls += ' tab-content';
16711 if (this.carousel) {
16712 cfg.cls += ' carousel slide';
16715 cls : 'carousel-inner',
16719 if(this.bullets && !Roo.isTouch){
16722 cls : 'carousel-bullets',
16726 if(this.bullets_cls){
16727 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16734 cfg.cn[0].cn.push(bullets);
16737 if(this.showarrow){
16738 cfg.cn[0].cn.push({
16740 class : 'carousel-arrow',
16744 class : 'carousel-prev',
16748 class : 'fa fa-chevron-left'
16754 class : 'carousel-next',
16758 class : 'fa fa-chevron-right'
16771 initEvents: function()
16773 if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16774 this.el.on("touchstart", this.onTouchStart, this);
16777 if(this.autoslide){
16780 this.slideFn = window.setInterval(function() {
16781 _this.showPanelNext();
16785 if(this.showarrow){
16786 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16787 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16793 onTouchStart : function(e, el, o)
16795 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16799 this.showPanelNext();
16802 getChildContainer : function()
16804 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16808 * register a Navigation item
16809 * @param {Roo.bootstrap.NavItem} the navitem to add
16811 register : function(item)
16813 this.tabs.push( item);
16814 item.navId = this.navId; // not really needed..
16819 getActivePanel : function()
16822 Roo.each(this.tabs, function(t) {
16832 getPanelByName : function(n)
16835 Roo.each(this.tabs, function(t) {
16836 if (t.tabId == n) {
16844 indexOfPanel : function(p)
16847 Roo.each(this.tabs, function(t,i) {
16848 if (t.tabId == p.tabId) {
16857 * show a specific panel
16858 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16859 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16861 showPanel : function (pan)
16863 if(this.transition || typeof(pan) == 'undefined'){
16864 Roo.log("waiting for the transitionend");
16868 if (typeof(pan) == 'number') {
16869 pan = this.tabs[pan];
16872 if (typeof(pan) == 'string') {
16873 pan = this.getPanelByName(pan);
16876 var cur = this.getActivePanel();
16879 Roo.log('pan or acitve pan is undefined');
16883 if (pan.tabId == this.getActivePanel().tabId) {
16887 if (false === cur.fireEvent('beforedeactivate')) {
16891 if(this.bullets > 0 && !Roo.isTouch){
16892 this.setActiveBullet(this.indexOfPanel(pan));
16895 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16897 this.transition = true;
16898 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16899 var lr = dir == 'next' ? 'left' : 'right';
16900 pan.el.addClass(dir); // or prev
16901 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16902 cur.el.addClass(lr); // or right
16903 pan.el.addClass(lr);
16906 cur.el.on('transitionend', function() {
16907 Roo.log("trans end?");
16909 pan.el.removeClass([lr,dir]);
16910 pan.setActive(true);
16912 cur.el.removeClass([lr]);
16913 cur.setActive(false);
16915 _this.transition = false;
16917 }, this, { single: true } );
16922 cur.setActive(false);
16923 pan.setActive(true);
16928 showPanelNext : function()
16930 var i = this.indexOfPanel(this.getActivePanel());
16932 if (i >= this.tabs.length - 1 && !this.autoslide) {
16936 if (i >= this.tabs.length - 1 && this.autoslide) {
16940 this.showPanel(this.tabs[i+1]);
16943 showPanelPrev : function()
16945 var i = this.indexOfPanel(this.getActivePanel());
16947 if (i < 1 && !this.autoslide) {
16951 if (i < 1 && this.autoslide) {
16952 i = this.tabs.length;
16955 this.showPanel(this.tabs[i-1]);
16959 addBullet: function()
16961 if(!this.bullets || Roo.isTouch){
16964 var ctr = this.el.select('.carousel-bullets',true).first();
16965 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16966 var bullet = ctr.createChild({
16967 cls : 'bullet bullet-' + i
16968 },ctr.dom.lastChild);
16973 bullet.on('click', (function(e, el, o, ii, t){
16975 e.preventDefault();
16977 this.showPanel(ii);
16979 if(this.autoslide && this.slideFn){
16980 clearInterval(this.slideFn);
16981 this.slideFn = window.setInterval(function() {
16982 _this.showPanelNext();
16986 }).createDelegate(this, [i, bullet], true));
16991 setActiveBullet : function(i)
16997 Roo.each(this.el.select('.bullet', true).elements, function(el){
16998 el.removeClass('selected');
17001 var bullet = this.el.select('.bullet-' + i, true).first();
17007 bullet.addClass('selected');
17018 Roo.apply(Roo.bootstrap.TabGroup, {
17022 * register a Navigation Group
17023 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17025 register : function(navgrp)
17027 this.groups[navgrp.navId] = navgrp;
17031 * fetch a Navigation Group based on the navigation ID
17032 * if one does not exist , it will get created.
17033 * @param {string} the navgroup to add
17034 * @returns {Roo.bootstrap.NavGroup} the navgroup
17036 get: function(navId) {
17037 if (typeof(this.groups[navId]) == 'undefined') {
17038 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17040 return this.groups[navId] ;
17055 * @class Roo.bootstrap.TabPanel
17056 * @extends Roo.bootstrap.Component
17057 * Bootstrap TabPanel class
17058 * @cfg {Boolean} active panel active
17059 * @cfg {String} html panel content
17060 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17061 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17062 * @cfg {String} href click to link..
17066 * Create a new TabPanel
17067 * @param {Object} config The config object
17070 Roo.bootstrap.TabPanel = function(config){
17071 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17075 * Fires when the active status changes
17076 * @param {Roo.bootstrap.TabPanel} this
17077 * @param {Boolean} state the new state
17082 * @event beforedeactivate
17083 * Fires before a tab is de-activated - can be used to do validation on a form.
17084 * @param {Roo.bootstrap.TabPanel} this
17085 * @return {Boolean} false if there is an error
17088 'beforedeactivate': true
17091 this.tabId = this.tabId || Roo.id();
17095 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17103 getAutoCreate : function(){
17106 // item is needed for carousel - not sure if it has any effect otherwise
17107 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17108 html: this.html || ''
17112 cfg.cls += ' active';
17116 cfg.tabId = this.tabId;
17123 initEvents: function()
17125 var p = this.parent();
17126 this.navId = this.navId || p.navId;
17128 if (typeof(this.navId) != 'undefined') {
17129 // not really needed.. but just in case.. parent should be a NavGroup.
17130 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17134 var i = tg.tabs.length - 1;
17136 if(this.active && tg.bullets > 0 && i < tg.bullets){
17137 tg.setActiveBullet(i);
17141 if(this.href.length){
17142 this.el.on('click', this.onClick, this);
17147 onRender : function(ct, position)
17149 // Roo.log("Call onRender: " + this.xtype);
17151 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17159 setActive: function(state)
17161 Roo.log("panel - set active " + this.tabId + "=" + state);
17163 this.active = state;
17165 this.el.removeClass('active');
17167 } else if (!this.el.hasClass('active')) {
17168 this.el.addClass('active');
17171 this.fireEvent('changed', this, state);
17174 onClick: function(e)
17176 e.preventDefault();
17178 window.location.href = this.href;
17195 * @class Roo.bootstrap.DateField
17196 * @extends Roo.bootstrap.Input
17197 * Bootstrap DateField class
17198 * @cfg {Number} weekStart default 0
17199 * @cfg {String} viewMode default empty, (months|years)
17200 * @cfg {String} minViewMode default empty, (months|years)
17201 * @cfg {Number} startDate default -Infinity
17202 * @cfg {Number} endDate default Infinity
17203 * @cfg {Boolean} todayHighlight default false
17204 * @cfg {Boolean} todayBtn default false
17205 * @cfg {Boolean} calendarWeeks default false
17206 * @cfg {Object} daysOfWeekDisabled default empty
17207 * @cfg {Boolean} singleMode default false (true | false)
17209 * @cfg {Boolean} keyboardNavigation default true
17210 * @cfg {String} language default en
17213 * Create a new DateField
17214 * @param {Object} config The config object
17217 Roo.bootstrap.DateField = function(config){
17218 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17222 * Fires when this field show.
17223 * @param {Roo.bootstrap.DateField} this
17224 * @param {Mixed} date The date value
17229 * Fires when this field hide.
17230 * @param {Roo.bootstrap.DateField} this
17231 * @param {Mixed} date The date value
17236 * Fires when select a date.
17237 * @param {Roo.bootstrap.DateField} this
17238 * @param {Mixed} date The date value
17242 * @event beforeselect
17243 * Fires when before select a date.
17244 * @param {Roo.bootstrap.DateField} this
17245 * @param {Mixed} date The date value
17247 beforeselect : true
17251 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
17254 * @cfg {String} format
17255 * The default date format string which can be overriden for localization support. The format must be
17256 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17260 * @cfg {String} altFormats
17261 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17262 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17264 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17272 todayHighlight : false,
17278 keyboardNavigation: true,
17280 calendarWeeks: false,
17282 startDate: -Infinity,
17286 daysOfWeekDisabled: [],
17290 singleMode : false,
17292 UTCDate: function()
17294 return new Date(Date.UTC.apply(Date, arguments));
17297 UTCToday: function()
17299 var today = new Date();
17300 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17303 getDate: function() {
17304 var d = this.getUTCDate();
17305 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17308 getUTCDate: function() {
17312 setDate: function(d) {
17313 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17316 setUTCDate: function(d) {
17318 this.setValue(this.formatDate(this.date));
17321 onRender: function(ct, position)
17324 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17326 this.language = this.language || 'en';
17327 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17328 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17330 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17331 this.format = this.format || 'm/d/y';
17332 this.isInline = false;
17333 this.isInput = true;
17334 this.component = this.el.select('.add-on', true).first() || false;
17335 this.component = (this.component && this.component.length === 0) ? false : this.component;
17336 this.hasInput = this.component && this.inputEl().length;
17338 if (typeof(this.minViewMode === 'string')) {
17339 switch (this.minViewMode) {
17341 this.minViewMode = 1;
17344 this.minViewMode = 2;
17347 this.minViewMode = 0;
17352 if (typeof(this.viewMode === 'string')) {
17353 switch (this.viewMode) {
17366 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17368 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17370 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17372 this.picker().on('mousedown', this.onMousedown, this);
17373 this.picker().on('click', this.onClick, this);
17375 this.picker().addClass('datepicker-dropdown');
17377 this.startViewMode = this.viewMode;
17379 if(this.singleMode){
17380 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17381 v.setVisibilityMode(Roo.Element.DISPLAY);
17385 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17386 v.setStyle('width', '189px');
17390 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17391 if(!this.calendarWeeks){
17396 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17397 v.attr('colspan', function(i, val){
17398 return parseInt(val) + 1;
17403 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17405 this.setStartDate(this.startDate);
17406 this.setEndDate(this.endDate);
17408 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17415 if(this.isInline) {
17420 picker : function()
17422 return this.pickerEl;
17423 // return this.el.select('.datepicker', true).first();
17426 fillDow: function()
17428 var dowCnt = this.weekStart;
17437 if(this.calendarWeeks){
17445 while (dowCnt < this.weekStart + 7) {
17449 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17453 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17456 fillMonths: function()
17459 var months = this.picker().select('>.datepicker-months td', true).first();
17461 months.dom.innerHTML = '';
17467 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17470 months.createChild(month);
17477 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;
17479 if (this.date < this.startDate) {
17480 this.viewDate = new Date(this.startDate);
17481 } else if (this.date > this.endDate) {
17482 this.viewDate = new Date(this.endDate);
17484 this.viewDate = new Date(this.date);
17492 var d = new Date(this.viewDate),
17493 year = d.getUTCFullYear(),
17494 month = d.getUTCMonth(),
17495 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17496 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17497 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17498 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17499 currentDate = this.date && this.date.valueOf(),
17500 today = this.UTCToday();
17502 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17504 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17506 // this.picker.select('>tfoot th.today').
17507 // .text(dates[this.language].today)
17508 // .toggle(this.todayBtn !== false);
17510 this.updateNavArrows();
17513 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17515 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17517 prevMonth.setUTCDate(day);
17519 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17521 var nextMonth = new Date(prevMonth);
17523 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17525 nextMonth = nextMonth.valueOf();
17527 var fillMonths = false;
17529 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17531 while(prevMonth.valueOf() < nextMonth) {
17534 if (prevMonth.getUTCDay() === this.weekStart) {
17536 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17544 if(this.calendarWeeks){
17545 // ISO 8601: First week contains first thursday.
17546 // ISO also states week starts on Monday, but we can be more abstract here.
17548 // Start of current week: based on weekstart/current date
17549 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17550 // Thursday of this week
17551 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17552 // First Thursday of year, year from thursday
17553 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17554 // Calendar week: ms between thursdays, div ms per day, div 7 days
17555 calWeek = (th - yth) / 864e5 / 7 + 1;
17557 fillMonths.cn.push({
17565 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17567 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17570 if (this.todayHighlight &&
17571 prevMonth.getUTCFullYear() == today.getFullYear() &&
17572 prevMonth.getUTCMonth() == today.getMonth() &&
17573 prevMonth.getUTCDate() == today.getDate()) {
17574 clsName += ' today';
17577 if (currentDate && prevMonth.valueOf() === currentDate) {
17578 clsName += ' active';
17581 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17582 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17583 clsName += ' disabled';
17586 fillMonths.cn.push({
17588 cls: 'day ' + clsName,
17589 html: prevMonth.getDate()
17592 prevMonth.setDate(prevMonth.getDate()+1);
17595 var currentYear = this.date && this.date.getUTCFullYear();
17596 var currentMonth = this.date && this.date.getUTCMonth();
17598 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17600 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17601 v.removeClass('active');
17603 if(currentYear === year && k === currentMonth){
17604 v.addClass('active');
17607 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17608 v.addClass('disabled');
17614 year = parseInt(year/10, 10) * 10;
17616 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17618 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17621 for (var i = -1; i < 11; i++) {
17622 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17624 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17632 showMode: function(dir)
17635 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17638 Roo.each(this.picker().select('>div',true).elements, function(v){
17639 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17642 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17647 if(this.isInline) {
17651 this.picker().removeClass(['bottom', 'top']);
17653 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17655 * place to the top of element!
17659 this.picker().addClass('top');
17660 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17665 this.picker().addClass('bottom');
17667 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17670 parseDate : function(value)
17672 if(!value || value instanceof Date){
17675 var v = Date.parseDate(value, this.format);
17676 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17677 v = Date.parseDate(value, 'Y-m-d');
17679 if(!v && this.altFormats){
17680 if(!this.altFormatsArray){
17681 this.altFormatsArray = this.altFormats.split("|");
17683 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17684 v = Date.parseDate(value, this.altFormatsArray[i]);
17690 formatDate : function(date, fmt)
17692 return (!date || !(date instanceof Date)) ?
17693 date : date.dateFormat(fmt || this.format);
17696 onFocus : function()
17698 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17702 onBlur : function()
17704 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17706 var d = this.inputEl().getValue();
17715 this.picker().show();
17719 this.fireEvent('show', this, this.date);
17724 if(this.isInline) {
17727 this.picker().hide();
17728 this.viewMode = this.startViewMode;
17731 this.fireEvent('hide', this, this.date);
17735 onMousedown: function(e)
17737 e.stopPropagation();
17738 e.preventDefault();
17743 Roo.bootstrap.DateField.superclass.keyup.call(this);
17747 setValue: function(v)
17749 if(this.fireEvent('beforeselect', this, v) !== false){
17750 var d = new Date(this.parseDate(v) ).clearTime();
17752 if(isNaN(d.getTime())){
17753 this.date = this.viewDate = '';
17754 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17758 v = this.formatDate(d);
17760 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17762 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17766 this.fireEvent('select', this, this.date);
17770 getValue: function()
17772 return this.formatDate(this.date);
17775 fireKey: function(e)
17777 if (!this.picker().isVisible()){
17778 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17784 var dateChanged = false,
17786 newDate, newViewDate;
17791 e.preventDefault();
17795 if (!this.keyboardNavigation) {
17798 dir = e.keyCode == 37 ? -1 : 1;
17801 newDate = this.moveYear(this.date, dir);
17802 newViewDate = this.moveYear(this.viewDate, dir);
17803 } else if (e.shiftKey){
17804 newDate = this.moveMonth(this.date, dir);
17805 newViewDate = this.moveMonth(this.viewDate, dir);
17807 newDate = new Date(this.date);
17808 newDate.setUTCDate(this.date.getUTCDate() + dir);
17809 newViewDate = new Date(this.viewDate);
17810 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17812 if (this.dateWithinRange(newDate)){
17813 this.date = newDate;
17814 this.viewDate = newViewDate;
17815 this.setValue(this.formatDate(this.date));
17817 e.preventDefault();
17818 dateChanged = true;
17823 if (!this.keyboardNavigation) {
17826 dir = e.keyCode == 38 ? -1 : 1;
17828 newDate = this.moveYear(this.date, dir);
17829 newViewDate = this.moveYear(this.viewDate, dir);
17830 } else if (e.shiftKey){
17831 newDate = this.moveMonth(this.date, dir);
17832 newViewDate = this.moveMonth(this.viewDate, dir);
17834 newDate = new Date(this.date);
17835 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17836 newViewDate = new Date(this.viewDate);
17837 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17839 if (this.dateWithinRange(newDate)){
17840 this.date = newDate;
17841 this.viewDate = newViewDate;
17842 this.setValue(this.formatDate(this.date));
17844 e.preventDefault();
17845 dateChanged = true;
17849 this.setValue(this.formatDate(this.date));
17851 e.preventDefault();
17854 this.setValue(this.formatDate(this.date));
17868 onClick: function(e)
17870 e.stopPropagation();
17871 e.preventDefault();
17873 var target = e.getTarget();
17875 if(target.nodeName.toLowerCase() === 'i'){
17876 target = Roo.get(target).dom.parentNode;
17879 var nodeName = target.nodeName;
17880 var className = target.className;
17881 var html = target.innerHTML;
17882 //Roo.log(nodeName);
17884 switch(nodeName.toLowerCase()) {
17886 switch(className) {
17892 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17893 switch(this.viewMode){
17895 this.viewDate = this.moveMonth(this.viewDate, dir);
17899 this.viewDate = this.moveYear(this.viewDate, dir);
17905 var date = new Date();
17906 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17908 this.setValue(this.formatDate(this.date));
17915 if (className.indexOf('disabled') < 0) {
17916 this.viewDate.setUTCDate(1);
17917 if (className.indexOf('month') > -1) {
17918 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17920 var year = parseInt(html, 10) || 0;
17921 this.viewDate.setUTCFullYear(year);
17925 if(this.singleMode){
17926 this.setValue(this.formatDate(this.viewDate));
17937 //Roo.log(className);
17938 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17939 var day = parseInt(html, 10) || 1;
17940 var year = this.viewDate.getUTCFullYear(),
17941 month = this.viewDate.getUTCMonth();
17943 if (className.indexOf('old') > -1) {
17950 } else if (className.indexOf('new') > -1) {
17958 //Roo.log([year,month,day]);
17959 this.date = this.UTCDate(year, month, day,0,0,0,0);
17960 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17962 //Roo.log(this.formatDate(this.date));
17963 this.setValue(this.formatDate(this.date));
17970 setStartDate: function(startDate)
17972 this.startDate = startDate || -Infinity;
17973 if (this.startDate !== -Infinity) {
17974 this.startDate = this.parseDate(this.startDate);
17977 this.updateNavArrows();
17980 setEndDate: function(endDate)
17982 this.endDate = endDate || Infinity;
17983 if (this.endDate !== Infinity) {
17984 this.endDate = this.parseDate(this.endDate);
17987 this.updateNavArrows();
17990 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17992 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17993 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17994 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17996 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17997 return parseInt(d, 10);
18000 this.updateNavArrows();
18003 updateNavArrows: function()
18005 if(this.singleMode){
18009 var d = new Date(this.viewDate),
18010 year = d.getUTCFullYear(),
18011 month = d.getUTCMonth();
18013 Roo.each(this.picker().select('.prev', true).elements, function(v){
18015 switch (this.viewMode) {
18018 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18024 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18031 Roo.each(this.picker().select('.next', true).elements, function(v){
18033 switch (this.viewMode) {
18036 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18042 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18050 moveMonth: function(date, dir)
18055 var new_date = new Date(date.valueOf()),
18056 day = new_date.getUTCDate(),
18057 month = new_date.getUTCMonth(),
18058 mag = Math.abs(dir),
18060 dir = dir > 0 ? 1 : -1;
18063 // If going back one month, make sure month is not current month
18064 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18066 return new_date.getUTCMonth() == month;
18068 // If going forward one month, make sure month is as expected
18069 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18071 return new_date.getUTCMonth() != new_month;
18073 new_month = month + dir;
18074 new_date.setUTCMonth(new_month);
18075 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18076 if (new_month < 0 || new_month > 11) {
18077 new_month = (new_month + 12) % 12;
18080 // For magnitudes >1, move one month at a time...
18081 for (var i=0; i<mag; i++) {
18082 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18083 new_date = this.moveMonth(new_date, dir);
18085 // ...then reset the day, keeping it in the new month
18086 new_month = new_date.getUTCMonth();
18087 new_date.setUTCDate(day);
18089 return new_month != new_date.getUTCMonth();
18092 // Common date-resetting loop -- if date is beyond end of month, make it
18095 new_date.setUTCDate(--day);
18096 new_date.setUTCMonth(new_month);
18101 moveYear: function(date, dir)
18103 return this.moveMonth(date, dir*12);
18106 dateWithinRange: function(date)
18108 return date >= this.startDate && date <= this.endDate;
18114 this.picker().remove();
18117 validateValue : function(value)
18119 if(value.length < 1) {
18120 if(this.allowBlank){
18126 if(value.length < this.minLength){
18129 if(value.length > this.maxLength){
18133 var vt = Roo.form.VTypes;
18134 if(!vt[this.vtype](value, this)){
18138 if(typeof this.validator == "function"){
18139 var msg = this.validator(value);
18145 if(this.regex && !this.regex.test(value)){
18149 if(typeof(this.parseDate(value)) == 'undefined'){
18153 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18157 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18167 Roo.apply(Roo.bootstrap.DateField, {
18178 html: '<i class="fa fa-arrow-left"/>'
18188 html: '<i class="fa fa-arrow-right"/>'
18230 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18231 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18232 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18233 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18234 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18247 navFnc: 'FullYear',
18252 navFnc: 'FullYear',
18257 Roo.apply(Roo.bootstrap.DateField, {
18261 cls: 'datepicker dropdown-menu roo-dynamic',
18265 cls: 'datepicker-days',
18269 cls: 'table-condensed',
18271 Roo.bootstrap.DateField.head,
18275 Roo.bootstrap.DateField.footer
18282 cls: 'datepicker-months',
18286 cls: 'table-condensed',
18288 Roo.bootstrap.DateField.head,
18289 Roo.bootstrap.DateField.content,
18290 Roo.bootstrap.DateField.footer
18297 cls: 'datepicker-years',
18301 cls: 'table-condensed',
18303 Roo.bootstrap.DateField.head,
18304 Roo.bootstrap.DateField.content,
18305 Roo.bootstrap.DateField.footer
18324 * @class Roo.bootstrap.TimeField
18325 * @extends Roo.bootstrap.Input
18326 * Bootstrap DateField class
18330 * Create a new TimeField
18331 * @param {Object} config The config object
18334 Roo.bootstrap.TimeField = function(config){
18335 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18339 * Fires when this field show.
18340 * @param {Roo.bootstrap.DateField} thisthis
18341 * @param {Mixed} date The date value
18346 * Fires when this field hide.
18347 * @param {Roo.bootstrap.DateField} this
18348 * @param {Mixed} date The date value
18353 * Fires when select a date.
18354 * @param {Roo.bootstrap.DateField} this
18355 * @param {Mixed} date The date value
18361 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18364 * @cfg {String} format
18365 * The default time format string which can be overriden for localization support. The format must be
18366 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18370 onRender: function(ct, position)
18373 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18375 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18377 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18379 this.pop = this.picker().select('>.datepicker-time',true).first();
18380 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18382 this.picker().on('mousedown', this.onMousedown, this);
18383 this.picker().on('click', this.onClick, this);
18385 this.picker().addClass('datepicker-dropdown');
18390 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18391 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18392 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18393 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18394 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18395 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18399 fireKey: function(e){
18400 if (!this.picker().isVisible()){
18401 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18407 e.preventDefault();
18415 this.onTogglePeriod();
18418 this.onIncrementMinutes();
18421 this.onDecrementMinutes();
18430 onClick: function(e) {
18431 e.stopPropagation();
18432 e.preventDefault();
18435 picker : function()
18437 return this.el.select('.datepicker', true).first();
18440 fillTime: function()
18442 var time = this.pop.select('tbody', true).first();
18444 time.dom.innerHTML = '';
18459 cls: 'hours-up glyphicon glyphicon-chevron-up'
18479 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18500 cls: 'timepicker-hour',
18515 cls: 'timepicker-minute',
18530 cls: 'btn btn-primary period',
18552 cls: 'hours-down glyphicon glyphicon-chevron-down'
18572 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18590 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18597 var hours = this.time.getHours();
18598 var minutes = this.time.getMinutes();
18611 hours = hours - 12;
18615 hours = '0' + hours;
18619 minutes = '0' + minutes;
18622 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18623 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18624 this.pop.select('button', true).first().dom.innerHTML = period;
18630 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18632 var cls = ['bottom'];
18634 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18641 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18646 this.picker().addClass(cls.join('-'));
18650 Roo.each(cls, function(c){
18652 _this.picker().setTop(_this.inputEl().getHeight());
18656 _this.picker().setTop(0 - _this.picker().getHeight());
18661 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18665 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18672 onFocus : function()
18674 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18678 onBlur : function()
18680 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18686 this.picker().show();
18691 this.fireEvent('show', this, this.date);
18696 this.picker().hide();
18699 this.fireEvent('hide', this, this.date);
18702 setTime : function()
18705 this.setValue(this.time.format(this.format));
18707 this.fireEvent('select', this, this.date);
18712 onMousedown: function(e){
18713 e.stopPropagation();
18714 e.preventDefault();
18717 onIncrementHours: function()
18719 Roo.log('onIncrementHours');
18720 this.time = this.time.add(Date.HOUR, 1);
18725 onDecrementHours: function()
18727 Roo.log('onDecrementHours');
18728 this.time = this.time.add(Date.HOUR, -1);
18732 onIncrementMinutes: function()
18734 Roo.log('onIncrementMinutes');
18735 this.time = this.time.add(Date.MINUTE, 1);
18739 onDecrementMinutes: function()
18741 Roo.log('onDecrementMinutes');
18742 this.time = this.time.add(Date.MINUTE, -1);
18746 onTogglePeriod: function()
18748 Roo.log('onTogglePeriod');
18749 this.time = this.time.add(Date.HOUR, 12);
18756 Roo.apply(Roo.bootstrap.TimeField, {
18786 cls: 'btn btn-info ok',
18798 Roo.apply(Roo.bootstrap.TimeField, {
18802 cls: 'datepicker dropdown-menu',
18806 cls: 'datepicker-time',
18810 cls: 'table-condensed',
18812 Roo.bootstrap.TimeField.content,
18813 Roo.bootstrap.TimeField.footer
18832 * @class Roo.bootstrap.MonthField
18833 * @extends Roo.bootstrap.Input
18834 * Bootstrap MonthField class
18836 * @cfg {String} language default en
18839 * Create a new MonthField
18840 * @param {Object} config The config object
18843 Roo.bootstrap.MonthField = function(config){
18844 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18849 * Fires when this field show.
18850 * @param {Roo.bootstrap.MonthField} this
18851 * @param {Mixed} date The date value
18856 * Fires when this field hide.
18857 * @param {Roo.bootstrap.MonthField} this
18858 * @param {Mixed} date The date value
18863 * Fires when select a date.
18864 * @param {Roo.bootstrap.MonthField} this
18865 * @param {String} oldvalue The old value
18866 * @param {String} newvalue The new value
18872 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18874 onRender: function(ct, position)
18877 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18879 this.language = this.language || 'en';
18880 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18881 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18883 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18884 this.isInline = false;
18885 this.isInput = true;
18886 this.component = this.el.select('.add-on', true).first() || false;
18887 this.component = (this.component && this.component.length === 0) ? false : this.component;
18888 this.hasInput = this.component && this.inputEL().length;
18890 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18892 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18894 this.picker().on('mousedown', this.onMousedown, this);
18895 this.picker().on('click', this.onClick, this);
18897 this.picker().addClass('datepicker-dropdown');
18899 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18900 v.setStyle('width', '189px');
18907 if(this.isInline) {
18913 setValue: function(v, suppressEvent)
18915 var o = this.getValue();
18917 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18921 if(suppressEvent !== true){
18922 this.fireEvent('select', this, o, v);
18927 getValue: function()
18932 onClick: function(e)
18934 e.stopPropagation();
18935 e.preventDefault();
18937 var target = e.getTarget();
18939 if(target.nodeName.toLowerCase() === 'i'){
18940 target = Roo.get(target).dom.parentNode;
18943 var nodeName = target.nodeName;
18944 var className = target.className;
18945 var html = target.innerHTML;
18947 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18951 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18953 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18959 picker : function()
18961 return this.pickerEl;
18964 fillMonths: function()
18967 var months = this.picker().select('>.datepicker-months td', true).first();
18969 months.dom.innerHTML = '';
18975 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18978 months.createChild(month);
18987 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18988 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18991 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18992 e.removeClass('active');
18994 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18995 e.addClass('active');
19002 if(this.isInline) {
19006 this.picker().removeClass(['bottom', 'top']);
19008 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19010 * place to the top of element!
19014 this.picker().addClass('top');
19015 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19020 this.picker().addClass('bottom');
19022 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19025 onFocus : function()
19027 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19031 onBlur : function()
19033 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19035 var d = this.inputEl().getValue();
19044 this.picker().show();
19045 this.picker().select('>.datepicker-months', true).first().show();
19049 this.fireEvent('show', this, this.date);
19054 if(this.isInline) {
19057 this.picker().hide();
19058 this.fireEvent('hide', this, this.date);
19062 onMousedown: function(e)
19064 e.stopPropagation();
19065 e.preventDefault();
19070 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19074 fireKey: function(e)
19076 if (!this.picker().isVisible()){
19077 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19088 e.preventDefault();
19092 dir = e.keyCode == 37 ? -1 : 1;
19094 this.vIndex = this.vIndex + dir;
19096 if(this.vIndex < 0){
19100 if(this.vIndex > 11){
19104 if(isNaN(this.vIndex)){
19108 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19114 dir = e.keyCode == 38 ? -1 : 1;
19116 this.vIndex = this.vIndex + dir * 4;
19118 if(this.vIndex < 0){
19122 if(this.vIndex > 11){
19126 if(isNaN(this.vIndex)){
19130 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19135 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19136 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19140 e.preventDefault();
19143 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19144 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19160 this.picker().remove();
19165 Roo.apply(Roo.bootstrap.MonthField, {
19184 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19185 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19190 Roo.apply(Roo.bootstrap.MonthField, {
19194 cls: 'datepicker dropdown-menu roo-dynamic',
19198 cls: 'datepicker-months',
19202 cls: 'table-condensed',
19204 Roo.bootstrap.DateField.content
19224 * @class Roo.bootstrap.CheckBox
19225 * @extends Roo.bootstrap.Input
19226 * Bootstrap CheckBox class
19228 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19229 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19230 * @cfg {String} boxLabel The text that appears beside the checkbox
19231 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19232 * @cfg {Boolean} checked initnal the element
19233 * @cfg {Boolean} inline inline the element (default false)
19234 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19237 * Create a new CheckBox
19238 * @param {Object} config The config object
19241 Roo.bootstrap.CheckBox = function(config){
19242 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19247 * Fires when the element is checked or unchecked.
19248 * @param {Roo.bootstrap.CheckBox} this This input
19249 * @param {Boolean} checked The new checked value
19256 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
19258 inputType: 'checkbox',
19266 getAutoCreate : function()
19268 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19274 cfg.cls = 'form-group ' + this.inputType; //input-group
19277 cfg.cls += ' ' + this.inputType + '-inline';
19283 type : this.inputType,
19284 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
19285 cls : 'roo-' + this.inputType, //'form-box',
19286 placeholder : this.placeholder || ''
19290 if (this.weight) { // Validity check?
19291 cfg.cls += " " + this.inputType + "-" + this.weight;
19294 if (this.disabled) {
19295 input.disabled=true;
19299 input.checked = this.checked;
19303 input.name = this.name;
19307 input.cls += ' input-' + this.size;
19312 ['xs','sm','md','lg'].map(function(size){
19313 if (settings[size]) {
19314 cfg.cls += ' col-' + size + '-' + settings[size];
19318 var inputblock = input;
19320 if (this.before || this.after) {
19323 cls : 'input-group',
19328 inputblock.cn.push({
19330 cls : 'input-group-addon',
19335 inputblock.cn.push(input);
19338 inputblock.cn.push({
19340 cls : 'input-group-addon',
19347 if (align ==='left' && this.fieldLabel.length) {
19348 // Roo.log("left and has label");
19354 cls : 'control-label col-md-' + this.labelWidth,
19355 html : this.fieldLabel
19359 cls : "col-md-" + (12 - this.labelWidth),
19366 } else if ( this.fieldLabel.length) {
19367 // Roo.log(" label");
19371 tag: this.boxLabel ? 'span' : 'label',
19373 cls: 'control-label box-input-label',
19374 //cls : 'input-group-addon',
19375 html : this.fieldLabel
19385 // Roo.log(" no label && no align");
19386 cfg.cn = [ inputblock ] ;
19392 var boxLabelCfg = {
19394 //'for': id, // box label is handled by onclick - so no for...
19396 html: this.boxLabel
19400 boxLabelCfg.tooltip = this.tooltip;
19403 cfg.cn.push(boxLabelCfg);
19413 * return the real input element.
19415 inputEl: function ()
19417 return this.el.select('input.roo-' + this.inputType,true).first();
19420 labelEl: function()
19422 return this.el.select('label.control-label',true).first();
19424 /* depricated... */
19428 return this.labelEl();
19431 boxLabelEl: function()
19433 return this.el.select('label.box-label',true).first();
19436 initEvents : function()
19438 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19440 this.inputEl().on('click', this.onClick, this);
19442 if (this.boxLabel) {
19443 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19446 this.startValue = this.getValue();
19449 Roo.bootstrap.CheckBox.register(this);
19453 onClick : function()
19455 this.setChecked(!this.checked);
19458 setChecked : function(state,suppressEvent)
19460 this.startValue = this.getValue();
19462 if(this.inputType == 'radio'){
19464 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19465 e.dom.checked = false;
19468 this.inputEl().dom.checked = true;
19470 this.inputEl().dom.value = this.inputValue;
19472 if(suppressEvent !== true){
19473 this.fireEvent('check', this, true);
19481 this.checked = state;
19483 this.inputEl().dom.checked = state;
19485 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19487 if(suppressEvent !== true){
19488 this.fireEvent('check', this, state);
19494 getValue : function()
19496 if(this.inputType == 'radio'){
19497 return this.getGroupValue();
19500 return this.inputEl().getValue();
19504 getGroupValue : function()
19506 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19510 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19513 setValue : function(v,suppressEvent)
19515 if(this.inputType == 'radio'){
19516 this.setGroupValue(v, suppressEvent);
19520 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19525 setGroupValue : function(v, suppressEvent)
19527 this.startValue = this.getValue();
19529 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19530 e.dom.checked = false;
19532 if(e.dom.value == v){
19533 e.dom.checked = true;
19537 if(suppressEvent !== true){
19538 this.fireEvent('check', this, true);
19546 validate : function()
19550 (this.inputType == 'radio' && this.validateRadio()) ||
19551 (this.inputType == 'checkbox' && this.validateCheckbox())
19557 this.markInvalid();
19561 validateRadio : function()
19565 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19566 if(!e.dom.checked){
19578 validateCheckbox : function()
19581 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19584 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19592 for(var i in group){
19597 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19604 * Mark this field as valid
19606 markValid : function()
19608 if(this.allowBlank){
19614 this.fireEvent('valid', this);
19616 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19619 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19626 if(this.inputType == 'radio'){
19627 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19628 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19629 e.findParent('.form-group', false, true).addClass(_this.validClass);
19636 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19637 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19641 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19647 for(var i in group){
19648 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19649 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19654 * Mark this field as invalid
19655 * @param {String} msg The validation message
19657 markInvalid : function(msg)
19659 if(this.allowBlank){
19665 this.fireEvent('invalid', this, msg);
19667 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19670 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19674 label.markInvalid();
19677 if(this.inputType == 'radio'){
19678 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19679 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19680 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19687 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19688 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19692 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19698 for(var i in group){
19699 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19700 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19707 Roo.apply(Roo.bootstrap.CheckBox, {
19712 * register a CheckBox Group
19713 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19715 register : function(checkbox)
19717 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19718 this.groups[checkbox.groupId] = {};
19721 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19725 this.groups[checkbox.groupId][checkbox.name] = checkbox;
19729 * fetch a CheckBox Group based on the group ID
19730 * @param {string} the group ID
19731 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19733 get: function(groupId) {
19734 if (typeof(this.groups[groupId]) == 'undefined') {
19738 return this.groups[groupId] ;
19750 *<div class="radio">
19752 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19753 Option one is this and that—be sure to include why it's great
19760 *<label class="radio-inline">fieldLabel</label>
19761 *<label class="radio-inline">
19762 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19770 * @class Roo.bootstrap.Radio
19771 * @extends Roo.bootstrap.CheckBox
19772 * Bootstrap Radio class
19775 * Create a new Radio
19776 * @param {Object} config The config object
19779 Roo.bootstrap.Radio = function(config){
19780 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19784 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19786 inputType: 'radio',
19790 getAutoCreate : function()
19792 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19793 align = align || 'left'; // default...
19800 tag : this.inline ? 'span' : 'div',
19805 var inline = this.inline ? ' radio-inline' : '';
19809 // does not need for, as we wrap the input with it..
19811 cls : 'control-label box-label' + inline,
19814 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19818 //cls : 'control-label' + inline,
19819 html : this.fieldLabel,
19820 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19829 type : this.inputType,
19830 //value : (!this.checked) ? this.valueOff : this.inputValue,
19831 value : this.inputValue,
19833 placeholder : this.placeholder || '' // ?? needed????
19836 if (this.weight) { // Validity check?
19837 input.cls += " radio-" + this.weight;
19839 if (this.disabled) {
19840 input.disabled=true;
19844 input.checked = this.checked;
19848 input.name = this.name;
19852 input.cls += ' input-' + this.size;
19855 //?? can span's inline have a width??
19858 ['xs','sm','md','lg'].map(function(size){
19859 if (settings[size]) {
19860 cfg.cls += ' col-' + size + '-' + settings[size];
19864 var inputblock = input;
19866 if (this.before || this.after) {
19869 cls : 'input-group',
19874 inputblock.cn.push({
19876 cls : 'input-group-addon',
19880 inputblock.cn.push(input);
19882 inputblock.cn.push({
19884 cls : 'input-group-addon',
19892 if (this.fieldLabel && this.fieldLabel.length) {
19893 cfg.cn.push(fieldLabel);
19896 // normal bootstrap puts the input inside the label.
19897 // however with our styled version - it has to go after the input.
19899 //lbl.cn.push(inputblock);
19903 cls: 'radio' + inline,
19910 cfg.cn.push( lblwrap);
19915 html: this.boxLabel
19924 initEvents : function()
19926 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19928 this.inputEl().on('click', this.onClick, this);
19929 if (this.boxLabel) {
19930 //Roo.log('find label');
19931 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19936 inputEl: function ()
19938 return this.el.select('input.roo-radio',true).first();
19940 onClick : function()
19943 this.setChecked(true);
19946 setChecked : function(state,suppressEvent)
19949 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19950 v.dom.checked = false;
19953 Roo.log(this.inputEl().dom);
19954 this.checked = state;
19955 this.inputEl().dom.checked = state;
19957 if(suppressEvent !== true){
19958 this.fireEvent('check', this, state);
19961 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19965 getGroupValue : function()
19968 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19969 if(v.dom.checked == true){
19970 value = v.dom.value;
19978 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19979 * @return {Mixed} value The field value
19981 getValue : function(){
19982 return this.getGroupValue();
19988 //<script type="text/javascript">
19991 * Based Ext JS Library 1.1.1
19992 * Copyright(c) 2006-2007, Ext JS, LLC.
19998 * @class Roo.HtmlEditorCore
19999 * @extends Roo.Component
20000 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20002 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20005 Roo.HtmlEditorCore = function(config){
20008 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20013 * @event initialize
20014 * Fires when the editor is fully initialized (including the iframe)
20015 * @param {Roo.HtmlEditorCore} this
20020 * Fires when the editor is first receives the focus. Any insertion must wait
20021 * until after this event.
20022 * @param {Roo.HtmlEditorCore} this
20026 * @event beforesync
20027 * Fires before the textarea is updated with content from the editor iframe. Return false
20028 * to cancel the sync.
20029 * @param {Roo.HtmlEditorCore} this
20030 * @param {String} html
20034 * @event beforepush
20035 * Fires before the iframe editor is updated with content from the textarea. Return false
20036 * to cancel the push.
20037 * @param {Roo.HtmlEditorCore} this
20038 * @param {String} html
20043 * Fires when the textarea is updated with content from the editor iframe.
20044 * @param {Roo.HtmlEditorCore} this
20045 * @param {String} html
20050 * Fires when the iframe editor is updated with content from the textarea.
20051 * @param {Roo.HtmlEditorCore} this
20052 * @param {String} html
20057 * @event editorevent
20058 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20059 * @param {Roo.HtmlEditorCore} this
20065 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20067 // defaults : white / black...
20068 this.applyBlacklists();
20075 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20079 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20085 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20090 * @cfg {Number} height (in pixels)
20094 * @cfg {Number} width (in pixels)
20099 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20102 stylesheets: false,
20107 // private properties
20108 validationEvent : false,
20110 initialized : false,
20112 sourceEditMode : false,
20113 onFocus : Roo.emptyFn,
20115 hideMode:'offsets',
20119 // blacklist + whitelisted elements..
20126 * Protected method that will not generally be called directly. It
20127 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20128 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20130 getDocMarkup : function(){
20134 // inherit styels from page...??
20135 if (this.stylesheets === false) {
20137 Roo.get(document.head).select('style').each(function(node) {
20138 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20141 Roo.get(document.head).select('link').each(function(node) {
20142 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20145 } else if (!this.stylesheets.length) {
20147 st = '<style type="text/css">' +
20148 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20154 st += '<style type="text/css">' +
20155 'IMG { cursor: pointer } ' +
20159 return '<html><head>' + st +
20160 //<style type="text/css">' +
20161 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20163 ' </head><body class="roo-htmleditor-body"></body></html>';
20167 onRender : function(ct, position)
20170 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20171 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20174 this.el.dom.style.border = '0 none';
20175 this.el.dom.setAttribute('tabIndex', -1);
20176 this.el.addClass('x-hidden hide');
20180 if(Roo.isIE){ // fix IE 1px bogus margin
20181 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20185 this.frameId = Roo.id();
20189 var iframe = this.owner.wrap.createChild({
20191 cls: 'form-control', // bootstrap..
20193 name: this.frameId,
20194 frameBorder : 'no',
20195 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20200 this.iframe = iframe.dom;
20202 this.assignDocWin();
20204 this.doc.designMode = 'on';
20207 this.doc.write(this.getDocMarkup());
20211 var task = { // must defer to wait for browser to be ready
20213 //console.log("run task?" + this.doc.readyState);
20214 this.assignDocWin();
20215 if(this.doc.body || this.doc.readyState == 'complete'){
20217 this.doc.designMode="on";
20221 Roo.TaskMgr.stop(task);
20222 this.initEditor.defer(10, this);
20229 Roo.TaskMgr.start(task);
20234 onResize : function(w, h)
20236 Roo.log('resize: ' +w + ',' + h );
20237 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20241 if(typeof w == 'number'){
20243 this.iframe.style.width = w + 'px';
20245 if(typeof h == 'number'){
20247 this.iframe.style.height = h + 'px';
20249 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20256 * Toggles the editor between standard and source edit mode.
20257 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20259 toggleSourceEdit : function(sourceEditMode){
20261 this.sourceEditMode = sourceEditMode === true;
20263 if(this.sourceEditMode){
20265 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20268 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20269 //this.iframe.className = '';
20272 //this.setSize(this.owner.wrap.getSize());
20273 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20280 * Protected method that will not generally be called directly. If you need/want
20281 * custom HTML cleanup, this is the method you should override.
20282 * @param {String} html The HTML to be cleaned
20283 * return {String} The cleaned HTML
20285 cleanHtml : function(html){
20286 html = String(html);
20287 if(html.length > 5){
20288 if(Roo.isSafari){ // strip safari nonsense
20289 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20292 if(html == ' '){
20299 * HTML Editor -> Textarea
20300 * Protected method that will not generally be called directly. Syncs the contents
20301 * of the editor iframe with the textarea.
20303 syncValue : function(){
20304 if(this.initialized){
20305 var bd = (this.doc.body || this.doc.documentElement);
20306 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20307 var html = bd.innerHTML;
20309 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20310 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20312 html = '<div style="'+m[0]+'">' + html + '</div>';
20315 html = this.cleanHtml(html);
20316 // fix up the special chars.. normaly like back quotes in word...
20317 // however we do not want to do this with chinese..
20318 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20319 var cc = b.charCodeAt();
20321 (cc >= 0x4E00 && cc < 0xA000 ) ||
20322 (cc >= 0x3400 && cc < 0x4E00 ) ||
20323 (cc >= 0xf900 && cc < 0xfb00 )
20329 if(this.owner.fireEvent('beforesync', this, html) !== false){
20330 this.el.dom.value = html;
20331 this.owner.fireEvent('sync', this, html);
20337 * Protected method that will not generally be called directly. Pushes the value of the textarea
20338 * into the iframe editor.
20340 pushValue : function(){
20341 if(this.initialized){
20342 var v = this.el.dom.value.trim();
20344 // if(v.length < 1){
20348 if(this.owner.fireEvent('beforepush', this, v) !== false){
20349 var d = (this.doc.body || this.doc.documentElement);
20351 this.cleanUpPaste();
20352 this.el.dom.value = d.innerHTML;
20353 this.owner.fireEvent('push', this, v);
20359 deferFocus : function(){
20360 this.focus.defer(10, this);
20364 focus : function(){
20365 if(this.win && !this.sourceEditMode){
20372 assignDocWin: function()
20374 var iframe = this.iframe;
20377 this.doc = iframe.contentWindow.document;
20378 this.win = iframe.contentWindow;
20380 // if (!Roo.get(this.frameId)) {
20383 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20384 // this.win = Roo.get(this.frameId).dom.contentWindow;
20386 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20390 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20391 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20396 initEditor : function(){
20397 //console.log("INIT EDITOR");
20398 this.assignDocWin();
20402 this.doc.designMode="on";
20404 this.doc.write(this.getDocMarkup());
20407 var dbody = (this.doc.body || this.doc.documentElement);
20408 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20409 // this copies styles from the containing element into thsi one..
20410 // not sure why we need all of this..
20411 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20413 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20414 //ss['background-attachment'] = 'fixed'; // w3c
20415 dbody.bgProperties = 'fixed'; // ie
20416 //Roo.DomHelper.applyStyles(dbody, ss);
20417 Roo.EventManager.on(this.doc, {
20418 //'mousedown': this.onEditorEvent,
20419 'mouseup': this.onEditorEvent,
20420 'dblclick': this.onEditorEvent,
20421 'click': this.onEditorEvent,
20422 'keyup': this.onEditorEvent,
20427 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20429 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20430 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20432 this.initialized = true;
20434 this.owner.fireEvent('initialize', this);
20439 onDestroy : function(){
20445 //for (var i =0; i < this.toolbars.length;i++) {
20446 // // fixme - ask toolbars for heights?
20447 // this.toolbars[i].onDestroy();
20450 //this.wrap.dom.innerHTML = '';
20451 //this.wrap.remove();
20456 onFirstFocus : function(){
20458 this.assignDocWin();
20461 this.activated = true;
20464 if(Roo.isGecko){ // prevent silly gecko errors
20466 var s = this.win.getSelection();
20467 if(!s.focusNode || s.focusNode.nodeType != 3){
20468 var r = s.getRangeAt(0);
20469 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20474 this.execCmd('useCSS', true);
20475 this.execCmd('styleWithCSS', false);
20478 this.owner.fireEvent('activate', this);
20482 adjustFont: function(btn){
20483 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20484 //if(Roo.isSafari){ // safari
20487 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20488 if(Roo.isSafari){ // safari
20489 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20490 v = (v < 10) ? 10 : v;
20491 v = (v > 48) ? 48 : v;
20492 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20497 v = Math.max(1, v+adjust);
20499 this.execCmd('FontSize', v );
20502 onEditorEvent : function(e)
20504 this.owner.fireEvent('editorevent', this, e);
20505 // this.updateToolbar();
20506 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20509 insertTag : function(tg)
20511 // could be a bit smarter... -> wrap the current selected tRoo..
20512 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20514 range = this.createRange(this.getSelection());
20515 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20516 wrappingNode.appendChild(range.extractContents());
20517 range.insertNode(wrappingNode);
20524 this.execCmd("formatblock", tg);
20528 insertText : function(txt)
20532 var range = this.createRange();
20533 range.deleteContents();
20534 //alert(Sender.getAttribute('label'));
20536 range.insertNode(this.doc.createTextNode(txt));
20542 * Executes a Midas editor command on the editor document and performs necessary focus and
20543 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20544 * @param {String} cmd The Midas command
20545 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20547 relayCmd : function(cmd, value){
20549 this.execCmd(cmd, value);
20550 this.owner.fireEvent('editorevent', this);
20551 //this.updateToolbar();
20552 this.owner.deferFocus();
20556 * Executes a Midas editor command directly on the editor document.
20557 * For visual commands, you should use {@link #relayCmd} instead.
20558 * <b>This should only be called after the editor is initialized.</b>
20559 * @param {String} cmd The Midas command
20560 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20562 execCmd : function(cmd, value){
20563 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20570 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20572 * @param {String} text | dom node..
20574 insertAtCursor : function(text)
20579 if(!this.activated){
20585 var r = this.doc.selection.createRange();
20596 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20600 // from jquery ui (MIT licenced)
20602 var win = this.win;
20604 if (win.getSelection && win.getSelection().getRangeAt) {
20605 range = win.getSelection().getRangeAt(0);
20606 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20607 range.insertNode(node);
20608 } else if (win.document.selection && win.document.selection.createRange) {
20609 // no firefox support
20610 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20611 win.document.selection.createRange().pasteHTML(txt);
20613 // no firefox support
20614 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20615 this.execCmd('InsertHTML', txt);
20624 mozKeyPress : function(e){
20626 var c = e.getCharCode(), cmd;
20629 c = String.fromCharCode(c).toLowerCase();
20643 this.cleanUpPaste.defer(100, this);
20651 e.preventDefault();
20659 fixKeys : function(){ // load time branching for fastest keydown performance
20661 return function(e){
20662 var k = e.getKey(), r;
20665 r = this.doc.selection.createRange();
20668 r.pasteHTML('    ');
20675 r = this.doc.selection.createRange();
20677 var target = r.parentElement();
20678 if(!target || target.tagName.toLowerCase() != 'li'){
20680 r.pasteHTML('<br />');
20686 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20687 this.cleanUpPaste.defer(100, this);
20693 }else if(Roo.isOpera){
20694 return function(e){
20695 var k = e.getKey();
20699 this.execCmd('InsertHTML','    ');
20702 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20703 this.cleanUpPaste.defer(100, this);
20708 }else if(Roo.isSafari){
20709 return function(e){
20710 var k = e.getKey();
20714 this.execCmd('InsertText','\t');
20718 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20719 this.cleanUpPaste.defer(100, this);
20727 getAllAncestors: function()
20729 var p = this.getSelectedNode();
20732 a.push(p); // push blank onto stack..
20733 p = this.getParentElement();
20737 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20741 a.push(this.doc.body);
20745 lastSelNode : false,
20748 getSelection : function()
20750 this.assignDocWin();
20751 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20754 getSelectedNode: function()
20756 // this may only work on Gecko!!!
20758 // should we cache this!!!!
20763 var range = this.createRange(this.getSelection()).cloneRange();
20766 var parent = range.parentElement();
20768 var testRange = range.duplicate();
20769 testRange.moveToElementText(parent);
20770 if (testRange.inRange(range)) {
20773 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20776 parent = parent.parentElement;
20781 // is ancestor a text element.
20782 var ac = range.commonAncestorContainer;
20783 if (ac.nodeType == 3) {
20784 ac = ac.parentNode;
20787 var ar = ac.childNodes;
20790 var other_nodes = [];
20791 var has_other_nodes = false;
20792 for (var i=0;i<ar.length;i++) {
20793 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20796 // fullly contained node.
20798 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20803 // probably selected..
20804 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20805 other_nodes.push(ar[i]);
20809 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20814 has_other_nodes = true;
20816 if (!nodes.length && other_nodes.length) {
20817 nodes= other_nodes;
20819 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20825 createRange: function(sel)
20827 // this has strange effects when using with
20828 // top toolbar - not sure if it's a great idea.
20829 //this.editor.contentWindow.focus();
20830 if (typeof sel != "undefined") {
20832 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20834 return this.doc.createRange();
20837 return this.doc.createRange();
20840 getParentElement: function()
20843 this.assignDocWin();
20844 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20846 var range = this.createRange(sel);
20849 var p = range.commonAncestorContainer;
20850 while (p.nodeType == 3) { // text node
20861 * Range intersection.. the hard stuff...
20865 * [ -- selected range --- ]
20869 * if end is before start or hits it. fail.
20870 * if start is after end or hits it fail.
20872 * if either hits (but other is outside. - then it's not
20878 // @see http://www.thismuchiknow.co.uk/?p=64.
20879 rangeIntersectsNode : function(range, node)
20881 var nodeRange = node.ownerDocument.createRange();
20883 nodeRange.selectNode(node);
20885 nodeRange.selectNodeContents(node);
20888 var rangeStartRange = range.cloneRange();
20889 rangeStartRange.collapse(true);
20891 var rangeEndRange = range.cloneRange();
20892 rangeEndRange.collapse(false);
20894 var nodeStartRange = nodeRange.cloneRange();
20895 nodeStartRange.collapse(true);
20897 var nodeEndRange = nodeRange.cloneRange();
20898 nodeEndRange.collapse(false);
20900 return rangeStartRange.compareBoundaryPoints(
20901 Range.START_TO_START, nodeEndRange) == -1 &&
20902 rangeEndRange.compareBoundaryPoints(
20903 Range.START_TO_START, nodeStartRange) == 1;
20907 rangeCompareNode : function(range, node)
20909 var nodeRange = node.ownerDocument.createRange();
20911 nodeRange.selectNode(node);
20913 nodeRange.selectNodeContents(node);
20917 range.collapse(true);
20919 nodeRange.collapse(true);
20921 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20922 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20924 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20926 var nodeIsBefore = ss == 1;
20927 var nodeIsAfter = ee == -1;
20929 if (nodeIsBefore && nodeIsAfter) {
20932 if (!nodeIsBefore && nodeIsAfter) {
20933 return 1; //right trailed.
20936 if (nodeIsBefore && !nodeIsAfter) {
20937 return 2; // left trailed.
20943 // private? - in a new class?
20944 cleanUpPaste : function()
20946 // cleans up the whole document..
20947 Roo.log('cleanuppaste');
20949 this.cleanUpChildren(this.doc.body);
20950 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20951 if (clean != this.doc.body.innerHTML) {
20952 this.doc.body.innerHTML = clean;
20957 cleanWordChars : function(input) {// change the chars to hex code
20958 var he = Roo.HtmlEditorCore;
20960 var output = input;
20961 Roo.each(he.swapCodes, function(sw) {
20962 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20964 output = output.replace(swapper, sw[1]);
20971 cleanUpChildren : function (n)
20973 if (!n.childNodes.length) {
20976 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20977 this.cleanUpChild(n.childNodes[i]);
20984 cleanUpChild : function (node)
20987 //console.log(node);
20988 if (node.nodeName == "#text") {
20989 // clean up silly Windows -- stuff?
20992 if (node.nodeName == "#comment") {
20993 node.parentNode.removeChild(node);
20994 // clean up silly Windows -- stuff?
20997 var lcname = node.tagName.toLowerCase();
20998 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20999 // whitelist of tags..
21001 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21003 node.parentNode.removeChild(node);
21008 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21010 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21011 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21013 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21014 // remove_keep_children = true;
21017 if (remove_keep_children) {
21018 this.cleanUpChildren(node);
21019 // inserts everything just before this node...
21020 while (node.childNodes.length) {
21021 var cn = node.childNodes[0];
21022 node.removeChild(cn);
21023 node.parentNode.insertBefore(cn, node);
21025 node.parentNode.removeChild(node);
21029 if (!node.attributes || !node.attributes.length) {
21030 this.cleanUpChildren(node);
21034 function cleanAttr(n,v)
21037 if (v.match(/^\./) || v.match(/^\//)) {
21040 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21043 if (v.match(/^#/)) {
21046 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21047 node.removeAttribute(n);
21051 var cwhite = this.cwhite;
21052 var cblack = this.cblack;
21054 function cleanStyle(n,v)
21056 if (v.match(/expression/)) { //XSS?? should we even bother..
21057 node.removeAttribute(n);
21061 var parts = v.split(/;/);
21064 Roo.each(parts, function(p) {
21065 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21069 var l = p.split(':').shift().replace(/\s+/g,'');
21070 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21072 if ( cwhite.length && cblack.indexOf(l) > -1) {
21073 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21074 //node.removeAttribute(n);
21078 // only allow 'c whitelisted system attributes'
21079 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21080 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21081 //node.removeAttribute(n);
21091 if (clean.length) {
21092 node.setAttribute(n, clean.join(';'));
21094 node.removeAttribute(n);
21100 for (var i = node.attributes.length-1; i > -1 ; i--) {
21101 var a = node.attributes[i];
21104 if (a.name.toLowerCase().substr(0,2)=='on') {
21105 node.removeAttribute(a.name);
21108 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21109 node.removeAttribute(a.name);
21112 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21113 cleanAttr(a.name,a.value); // fixme..
21116 if (a.name == 'style') {
21117 cleanStyle(a.name,a.value);
21120 /// clean up MS crap..
21121 // tecnically this should be a list of valid class'es..
21124 if (a.name == 'class') {
21125 if (a.value.match(/^Mso/)) {
21126 node.className = '';
21129 if (a.value.match(/body/)) {
21130 node.className = '';
21141 this.cleanUpChildren(node);
21147 * Clean up MS wordisms...
21149 cleanWord : function(node)
21154 this.cleanWord(this.doc.body);
21157 if (node.nodeName == "#text") {
21158 // clean up silly Windows -- stuff?
21161 if (node.nodeName == "#comment") {
21162 node.parentNode.removeChild(node);
21163 // clean up silly Windows -- stuff?
21167 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21168 node.parentNode.removeChild(node);
21172 // remove - but keep children..
21173 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21174 while (node.childNodes.length) {
21175 var cn = node.childNodes[0];
21176 node.removeChild(cn);
21177 node.parentNode.insertBefore(cn, node);
21179 node.parentNode.removeChild(node);
21180 this.iterateChildren(node, this.cleanWord);
21184 if (node.className.length) {
21186 var cn = node.className.split(/\W+/);
21188 Roo.each(cn, function(cls) {
21189 if (cls.match(/Mso[a-zA-Z]+/)) {
21194 node.className = cna.length ? cna.join(' ') : '';
21196 node.removeAttribute("class");
21200 if (node.hasAttribute("lang")) {
21201 node.removeAttribute("lang");
21204 if (node.hasAttribute("style")) {
21206 var styles = node.getAttribute("style").split(";");
21208 Roo.each(styles, function(s) {
21209 if (!s.match(/:/)) {
21212 var kv = s.split(":");
21213 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21216 // what ever is left... we allow.
21219 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21220 if (!nstyle.length) {
21221 node.removeAttribute('style');
21224 this.iterateChildren(node, this.cleanWord);
21230 * iterateChildren of a Node, calling fn each time, using this as the scole..
21231 * @param {DomNode} node node to iterate children of.
21232 * @param {Function} fn method of this class to call on each item.
21234 iterateChildren : function(node, fn)
21236 if (!node.childNodes.length) {
21239 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21240 fn.call(this, node.childNodes[i])
21246 * cleanTableWidths.
21248 * Quite often pasting from word etc.. results in tables with column and widths.
21249 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21252 cleanTableWidths : function(node)
21257 this.cleanTableWidths(this.doc.body);
21262 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21265 Roo.log(node.tagName);
21266 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21267 this.iterateChildren(node, this.cleanTableWidths);
21270 if (node.hasAttribute('width')) {
21271 node.removeAttribute('width');
21275 if (node.hasAttribute("style")) {
21278 var styles = node.getAttribute("style").split(";");
21280 Roo.each(styles, function(s) {
21281 if (!s.match(/:/)) {
21284 var kv = s.split(":");
21285 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21288 // what ever is left... we allow.
21291 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21292 if (!nstyle.length) {
21293 node.removeAttribute('style');
21297 this.iterateChildren(node, this.cleanTableWidths);
21305 domToHTML : function(currentElement, depth, nopadtext) {
21307 depth = depth || 0;
21308 nopadtext = nopadtext || false;
21310 if (!currentElement) {
21311 return this.domToHTML(this.doc.body);
21314 //Roo.log(currentElement);
21316 var allText = false;
21317 var nodeName = currentElement.nodeName;
21318 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21320 if (nodeName == '#text') {
21322 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21327 if (nodeName != 'BODY') {
21330 // Prints the node tagName, such as <A>, <IMG>, etc
21333 for(i = 0; i < currentElement.attributes.length;i++) {
21335 var aname = currentElement.attributes.item(i).name;
21336 if (!currentElement.attributes.item(i).value.length) {
21339 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21342 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21351 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21354 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21359 // Traverse the tree
21361 var currentElementChild = currentElement.childNodes.item(i);
21362 var allText = true;
21363 var innerHTML = '';
21365 while (currentElementChild) {
21366 // Formatting code (indent the tree so it looks nice on the screen)
21367 var nopad = nopadtext;
21368 if (lastnode == 'SPAN') {
21372 if (currentElementChild.nodeName == '#text') {
21373 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21374 toadd = nopadtext ? toadd : toadd.trim();
21375 if (!nopad && toadd.length > 80) {
21376 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21378 innerHTML += toadd;
21381 currentElementChild = currentElement.childNodes.item(i);
21387 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21389 // Recursively traverse the tree structure of the child node
21390 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21391 lastnode = currentElementChild.nodeName;
21393 currentElementChild=currentElement.childNodes.item(i);
21399 // The remaining code is mostly for formatting the tree
21400 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21405 ret+= "</"+tagName+">";
21411 applyBlacklists : function()
21413 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21414 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21418 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21419 if (b.indexOf(tag) > -1) {
21422 this.white.push(tag);
21426 Roo.each(w, function(tag) {
21427 if (b.indexOf(tag) > -1) {
21430 if (this.white.indexOf(tag) > -1) {
21433 this.white.push(tag);
21438 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21439 if (w.indexOf(tag) > -1) {
21442 this.black.push(tag);
21446 Roo.each(b, function(tag) {
21447 if (w.indexOf(tag) > -1) {
21450 if (this.black.indexOf(tag) > -1) {
21453 this.black.push(tag);
21458 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21459 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21463 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21464 if (b.indexOf(tag) > -1) {
21467 this.cwhite.push(tag);
21471 Roo.each(w, function(tag) {
21472 if (b.indexOf(tag) > -1) {
21475 if (this.cwhite.indexOf(tag) > -1) {
21478 this.cwhite.push(tag);
21483 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21484 if (w.indexOf(tag) > -1) {
21487 this.cblack.push(tag);
21491 Roo.each(b, function(tag) {
21492 if (w.indexOf(tag) > -1) {
21495 if (this.cblack.indexOf(tag) > -1) {
21498 this.cblack.push(tag);
21503 setStylesheets : function(stylesheets)
21505 if(typeof(stylesheets) == 'string'){
21506 Roo.get(this.iframe.contentDocument.head).createChild({
21508 rel : 'stylesheet',
21517 Roo.each(stylesheets, function(s) {
21522 Roo.get(_this.iframe.contentDocument.head).createChild({
21524 rel : 'stylesheet',
21533 removeStylesheets : function()
21537 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21542 // hide stuff that is not compatible
21556 * @event specialkey
21560 * @cfg {String} fieldClass @hide
21563 * @cfg {String} focusClass @hide
21566 * @cfg {String} autoCreate @hide
21569 * @cfg {String} inputType @hide
21572 * @cfg {String} invalidClass @hide
21575 * @cfg {String} invalidText @hide
21578 * @cfg {String} msgFx @hide
21581 * @cfg {String} validateOnBlur @hide
21585 Roo.HtmlEditorCore.white = [
21586 'area', 'br', 'img', 'input', 'hr', 'wbr',
21588 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21589 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21590 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21591 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21592 'table', 'ul', 'xmp',
21594 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21597 'dir', 'menu', 'ol', 'ul', 'dl',
21603 Roo.HtmlEditorCore.black = [
21604 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21606 'base', 'basefont', 'bgsound', 'blink', 'body',
21607 'frame', 'frameset', 'head', 'html', 'ilayer',
21608 'iframe', 'layer', 'link', 'meta', 'object',
21609 'script', 'style' ,'title', 'xml' // clean later..
21611 Roo.HtmlEditorCore.clean = [
21612 'script', 'style', 'title', 'xml'
21614 Roo.HtmlEditorCore.remove = [
21619 Roo.HtmlEditorCore.ablack = [
21623 Roo.HtmlEditorCore.aclean = [
21624 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21628 Roo.HtmlEditorCore.pwhite= [
21629 'http', 'https', 'mailto'
21632 // white listed style attributes.
21633 Roo.HtmlEditorCore.cwhite= [
21634 // 'text-align', /// default is to allow most things..
21640 // black listed style attributes.
21641 Roo.HtmlEditorCore.cblack= [
21642 // 'font-size' -- this can be set by the project
21646 Roo.HtmlEditorCore.swapCodes =[
21665 * @class Roo.bootstrap.HtmlEditor
21666 * @extends Roo.bootstrap.TextArea
21667 * Bootstrap HtmlEditor class
21670 * Create a new HtmlEditor
21671 * @param {Object} config The config object
21674 Roo.bootstrap.HtmlEditor = function(config){
21675 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21676 if (!this.toolbars) {
21677 this.toolbars = [];
21679 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21682 * @event initialize
21683 * Fires when the editor is fully initialized (including the iframe)
21684 * @param {HtmlEditor} this
21689 * Fires when the editor is first receives the focus. Any insertion must wait
21690 * until after this event.
21691 * @param {HtmlEditor} this
21695 * @event beforesync
21696 * Fires before the textarea is updated with content from the editor iframe. Return false
21697 * to cancel the sync.
21698 * @param {HtmlEditor} this
21699 * @param {String} html
21703 * @event beforepush
21704 * Fires before the iframe editor is updated with content from the textarea. Return false
21705 * to cancel the push.
21706 * @param {HtmlEditor} this
21707 * @param {String} html
21712 * Fires when the textarea is updated with content from the editor iframe.
21713 * @param {HtmlEditor} this
21714 * @param {String} html
21719 * Fires when the iframe editor is updated with content from the textarea.
21720 * @param {HtmlEditor} this
21721 * @param {String} html
21725 * @event editmodechange
21726 * Fires when the editor switches edit modes
21727 * @param {HtmlEditor} this
21728 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21730 editmodechange: true,
21732 * @event editorevent
21733 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21734 * @param {HtmlEditor} this
21738 * @event firstfocus
21739 * Fires when on first focus - needed by toolbars..
21740 * @param {HtmlEditor} this
21745 * Auto save the htmlEditor value as a file into Events
21746 * @param {HtmlEditor} this
21750 * @event savedpreview
21751 * preview the saved version of htmlEditor
21752 * @param {HtmlEditor} this
21759 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21763 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21768 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21773 * @cfg {Number} height (in pixels)
21777 * @cfg {Number} width (in pixels)
21782 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21785 stylesheets: false,
21790 // private properties
21791 validationEvent : false,
21793 initialized : false,
21796 onFocus : Roo.emptyFn,
21798 hideMode:'offsets',
21801 tbContainer : false,
21803 toolbarContainer :function() {
21804 return this.wrap.select('.x-html-editor-tb',true).first();
21808 * Protected method that will not generally be called directly. It
21809 * is called when the editor creates its toolbar. Override this method if you need to
21810 * add custom toolbar buttons.
21811 * @param {HtmlEditor} editor
21813 createToolbar : function(){
21815 Roo.log("create toolbars");
21817 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21818 this.toolbars[0].render(this.toolbarContainer());
21822 // if (!editor.toolbars || !editor.toolbars.length) {
21823 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21826 // for (var i =0 ; i < editor.toolbars.length;i++) {
21827 // editor.toolbars[i] = Roo.factory(
21828 // typeof(editor.toolbars[i]) == 'string' ?
21829 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21830 // Roo.bootstrap.HtmlEditor);
21831 // editor.toolbars[i].init(editor);
21837 onRender : function(ct, position)
21839 // Roo.log("Call onRender: " + this.xtype);
21841 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21843 this.wrap = this.inputEl().wrap({
21844 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21847 this.editorcore.onRender(ct, position);
21849 if (this.resizable) {
21850 this.resizeEl = new Roo.Resizable(this.wrap, {
21854 minHeight : this.height,
21855 height: this.height,
21856 handles : this.resizable,
21859 resize : function(r, w, h) {
21860 _t.onResize(w,h); // -something
21866 this.createToolbar(this);
21869 if(!this.width && this.resizable){
21870 this.setSize(this.wrap.getSize());
21872 if (this.resizeEl) {
21873 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21874 // should trigger onReize..
21880 onResize : function(w, h)
21882 Roo.log('resize: ' +w + ',' + h );
21883 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21887 if(this.inputEl() ){
21888 if(typeof w == 'number'){
21889 var aw = w - this.wrap.getFrameWidth('lr');
21890 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21893 if(typeof h == 'number'){
21894 var tbh = -11; // fixme it needs to tool bar size!
21895 for (var i =0; i < this.toolbars.length;i++) {
21896 // fixme - ask toolbars for heights?
21897 tbh += this.toolbars[i].el.getHeight();
21898 //if (this.toolbars[i].footer) {
21899 // tbh += this.toolbars[i].footer.el.getHeight();
21907 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21908 ah -= 5; // knock a few pixes off for look..
21909 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21913 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21914 this.editorcore.onResize(ew,eh);
21919 * Toggles the editor between standard and source edit mode.
21920 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21922 toggleSourceEdit : function(sourceEditMode)
21924 this.editorcore.toggleSourceEdit(sourceEditMode);
21926 if(this.editorcore.sourceEditMode){
21927 Roo.log('editor - showing textarea');
21930 // Roo.log(this.syncValue());
21932 this.inputEl().removeClass(['hide', 'x-hidden']);
21933 this.inputEl().dom.removeAttribute('tabIndex');
21934 this.inputEl().focus();
21936 Roo.log('editor - hiding textarea');
21938 // Roo.log(this.pushValue());
21941 this.inputEl().addClass(['hide', 'x-hidden']);
21942 this.inputEl().dom.setAttribute('tabIndex', -1);
21943 //this.deferFocus();
21946 if(this.resizable){
21947 this.setSize(this.wrap.getSize());
21950 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21953 // private (for BoxComponent)
21954 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21956 // private (for BoxComponent)
21957 getResizeEl : function(){
21961 // private (for BoxComponent)
21962 getPositionEl : function(){
21967 initEvents : function(){
21968 this.originalValue = this.getValue();
21972 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21975 // markInvalid : Roo.emptyFn,
21977 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21980 // clearInvalid : Roo.emptyFn,
21982 setValue : function(v){
21983 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21984 this.editorcore.pushValue();
21989 deferFocus : function(){
21990 this.focus.defer(10, this);
21994 focus : function(){
21995 this.editorcore.focus();
22001 onDestroy : function(){
22007 for (var i =0; i < this.toolbars.length;i++) {
22008 // fixme - ask toolbars for heights?
22009 this.toolbars[i].onDestroy();
22012 this.wrap.dom.innerHTML = '';
22013 this.wrap.remove();
22018 onFirstFocus : function(){
22019 //Roo.log("onFirstFocus");
22020 this.editorcore.onFirstFocus();
22021 for (var i =0; i < this.toolbars.length;i++) {
22022 this.toolbars[i].onFirstFocus();
22028 syncValue : function()
22030 this.editorcore.syncValue();
22033 pushValue : function()
22035 this.editorcore.pushValue();
22039 // hide stuff that is not compatible
22053 * @event specialkey
22057 * @cfg {String} fieldClass @hide
22060 * @cfg {String} focusClass @hide
22063 * @cfg {String} autoCreate @hide
22066 * @cfg {String} inputType @hide
22069 * @cfg {String} invalidClass @hide
22072 * @cfg {String} invalidText @hide
22075 * @cfg {String} msgFx @hide
22078 * @cfg {String} validateOnBlur @hide
22087 Roo.namespace('Roo.bootstrap.htmleditor');
22089 * @class Roo.bootstrap.HtmlEditorToolbar1
22094 new Roo.bootstrap.HtmlEditor({
22097 new Roo.bootstrap.HtmlEditorToolbar1({
22098 disable : { fonts: 1 , format: 1, ..., ... , ...],
22104 * @cfg {Object} disable List of elements to disable..
22105 * @cfg {Array} btns List of additional buttons.
22109 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22112 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22115 Roo.apply(this, config);
22117 // default disabled, based on 'good practice'..
22118 this.disable = this.disable || {};
22119 Roo.applyIf(this.disable, {
22122 specialElements : true
22124 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22126 this.editor = config.editor;
22127 this.editorcore = config.editor.editorcore;
22129 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22131 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22132 // dont call parent... till later.
22134 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
22139 editorcore : false,
22144 "h1","h2","h3","h4","h5","h6",
22146 "abbr", "acronym", "address", "cite", "samp", "var",
22150 onRender : function(ct, position)
22152 // Roo.log("Call onRender: " + this.xtype);
22154 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22156 this.el.dom.style.marginBottom = '0';
22158 var editorcore = this.editorcore;
22159 var editor= this.editor;
22162 var btn = function(id,cmd , toggle, handler){
22164 var event = toggle ? 'toggle' : 'click';
22169 xns: Roo.bootstrap,
22172 enableToggle:toggle !== false,
22174 pressed : toggle ? false : null,
22177 a.listeners[toggle ? 'toggle' : 'click'] = function() {
22178 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
22187 xns: Roo.bootstrap,
22188 glyphicon : 'font',
22192 xns: Roo.bootstrap,
22196 Roo.each(this.formats, function(f) {
22197 style.menu.items.push({
22199 xns: Roo.bootstrap,
22200 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22205 editorcore.insertTag(this.tagname);
22212 children.push(style);
22215 btn('bold',false,true);
22216 btn('italic',false,true);
22217 btn('align-left', 'justifyleft',true);
22218 btn('align-center', 'justifycenter',true);
22219 btn('align-right' , 'justifyright',true);
22220 btn('link', false, false, function(btn) {
22221 //Roo.log("create link?");
22222 var url = prompt(this.createLinkText, this.defaultLinkValue);
22223 if(url && url != 'http:/'+'/'){
22224 this.editorcore.relayCmd('createlink', url);
22227 btn('list','insertunorderedlist',true);
22228 btn('pencil', false,true, function(btn){
22231 this.toggleSourceEdit(btn.pressed);
22237 xns: Roo.bootstrap,
22242 xns: Roo.bootstrap,
22247 cog.menu.items.push({
22249 xns: Roo.bootstrap,
22250 html : Clean styles,
22255 editorcore.insertTag(this.tagname);
22264 this.xtype = 'NavSimplebar';
22266 for(var i=0;i< children.length;i++) {
22268 this.buttons.add(this.addxtypeChild(children[i]));
22272 editor.on('editorevent', this.updateToolbar, this);
22274 onBtnClick : function(id)
22276 this.editorcore.relayCmd(id);
22277 this.editorcore.focus();
22281 * Protected method that will not generally be called directly. It triggers
22282 * a toolbar update by reading the markup state of the current selection in the editor.
22284 updateToolbar: function(){
22286 if(!this.editorcore.activated){
22287 this.editor.onFirstFocus(); // is this neeed?
22291 var btns = this.buttons;
22292 var doc = this.editorcore.doc;
22293 btns.get('bold').setActive(doc.queryCommandState('bold'));
22294 btns.get('italic').setActive(doc.queryCommandState('italic'));
22295 //btns.get('underline').setActive(doc.queryCommandState('underline'));
22297 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22298 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22299 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22301 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22302 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22305 var ans = this.editorcore.getAllAncestors();
22306 if (this.formatCombo) {
22309 var store = this.formatCombo.store;
22310 this.formatCombo.setValue("");
22311 for (var i =0; i < ans.length;i++) {
22312 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22314 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22322 // hides menus... - so this cant be on a menu...
22323 Roo.bootstrap.MenuMgr.hideAll();
22325 Roo.bootstrap.MenuMgr.hideAll();
22326 //this.editorsyncValue();
22328 onFirstFocus: function() {
22329 this.buttons.each(function(item){
22333 toggleSourceEdit : function(sourceEditMode){
22336 if(sourceEditMode){
22337 Roo.log("disabling buttons");
22338 this.buttons.each( function(item){
22339 if(item.cmd != 'pencil'){
22345 Roo.log("enabling buttons");
22346 if(this.editorcore.initialized){
22347 this.buttons.each( function(item){
22353 Roo.log("calling toggole on editor");
22354 // tell the editor that it's been pressed..
22355 this.editor.toggleSourceEdit(sourceEditMode);
22365 * @class Roo.bootstrap.Table.AbstractSelectionModel
22366 * @extends Roo.util.Observable
22367 * Abstract base class for grid SelectionModels. It provides the interface that should be
22368 * implemented by descendant classes. This class should not be directly instantiated.
22371 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22372 this.locked = false;
22373 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22377 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22378 /** @ignore Called by the grid automatically. Do not call directly. */
22379 init : function(grid){
22385 * Locks the selections.
22388 this.locked = true;
22392 * Unlocks the selections.
22394 unlock : function(){
22395 this.locked = false;
22399 * Returns true if the selections are locked.
22400 * @return {Boolean}
22402 isLocked : function(){
22403 return this.locked;
22407 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22408 * @class Roo.bootstrap.Table.RowSelectionModel
22409 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22410 * It supports multiple selections and keyboard selection/navigation.
22412 * @param {Object} config
22415 Roo.bootstrap.Table.RowSelectionModel = function(config){
22416 Roo.apply(this, config);
22417 this.selections = new Roo.util.MixedCollection(false, function(o){
22422 this.lastActive = false;
22426 * @event selectionchange
22427 * Fires when the selection changes
22428 * @param {SelectionModel} this
22430 "selectionchange" : true,
22432 * @event afterselectionchange
22433 * Fires after the selection changes (eg. by key press or clicking)
22434 * @param {SelectionModel} this
22436 "afterselectionchange" : true,
22438 * @event beforerowselect
22439 * Fires when a row is selected being selected, return false to cancel.
22440 * @param {SelectionModel} this
22441 * @param {Number} rowIndex The selected index
22442 * @param {Boolean} keepExisting False if other selections will be cleared
22444 "beforerowselect" : true,
22447 * Fires when a row is selected.
22448 * @param {SelectionModel} this
22449 * @param {Number} rowIndex The selected index
22450 * @param {Roo.data.Record} r The record
22452 "rowselect" : true,
22454 * @event rowdeselect
22455 * Fires when a row is deselected.
22456 * @param {SelectionModel} this
22457 * @param {Number} rowIndex The selected index
22459 "rowdeselect" : true
22461 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22462 this.locked = false;
22465 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22467 * @cfg {Boolean} singleSelect
22468 * True to allow selection of only one row at a time (defaults to false)
22470 singleSelect : false,
22473 initEvents : function()
22476 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22477 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
22478 //}else{ // allow click to work like normal
22479 // this.grid.on("rowclick", this.handleDragableRowClick, this);
22481 this.grid.on("rowclick", this.handleMouseDown, this);
22483 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22484 "up" : function(e){
22486 this.selectPrevious(e.shiftKey);
22487 }else if(this.last !== false && this.lastActive !== false){
22488 var last = this.last;
22489 this.selectRange(this.last, this.lastActive-1);
22490 this.grid.getView().focusRow(this.lastActive);
22491 if(last !== false){
22495 this.selectFirstRow();
22497 this.fireEvent("afterselectionchange", this);
22499 "down" : function(e){
22501 this.selectNext(e.shiftKey);
22502 }else if(this.last !== false && this.lastActive !== false){
22503 var last = this.last;
22504 this.selectRange(this.last, this.lastActive+1);
22505 this.grid.getView().focusRow(this.lastActive);
22506 if(last !== false){
22510 this.selectFirstRow();
22512 this.fireEvent("afterselectionchange", this);
22517 var view = this.grid.view;
22518 view.on("refresh", this.onRefresh, this);
22519 view.on("rowupdated", this.onRowUpdated, this);
22520 view.on("rowremoved", this.onRemove, this);
22525 onRefresh : function(){
22526 var ds = this.grid.dataSource, i, v = this.grid.view;
22527 var s = this.selections;
22528 s.each(function(r){
22529 if((i = ds.indexOfId(r.id)) != -1){
22538 onRemove : function(v, index, r){
22539 this.selections.remove(r);
22543 onRowUpdated : function(v, index, r){
22544 if(this.isSelected(r)){
22545 v.onRowSelect(index);
22551 * @param {Array} records The records to select
22552 * @param {Boolean} keepExisting (optional) True to keep existing selections
22554 selectRecords : function(records, keepExisting){
22556 this.clearSelections();
22558 var ds = this.grid.dataSource;
22559 for(var i = 0, len = records.length; i < len; i++){
22560 this.selectRow(ds.indexOf(records[i]), true);
22565 * Gets the number of selected rows.
22568 getCount : function(){
22569 return this.selections.length;
22573 * Selects the first row in the grid.
22575 selectFirstRow : function(){
22580 * Select the last row.
22581 * @param {Boolean} keepExisting (optional) True to keep existing selections
22583 selectLastRow : function(keepExisting){
22584 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22588 * Selects the row immediately following the last selected row.
22589 * @param {Boolean} keepExisting (optional) True to keep existing selections
22591 selectNext : function(keepExisting){
22592 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22593 this.selectRow(this.last+1, keepExisting);
22594 this.grid.getView().focusRow(this.last);
22599 * Selects the row that precedes the last selected row.
22600 * @param {Boolean} keepExisting (optional) True to keep existing selections
22602 selectPrevious : function(keepExisting){
22604 this.selectRow(this.last-1, keepExisting);
22605 this.grid.getView().focusRow(this.last);
22610 * Returns the selected records
22611 * @return {Array} Array of selected records
22613 getSelections : function(){
22614 return [].concat(this.selections.items);
22618 * Returns the first selected record.
22621 getSelected : function(){
22622 return this.selections.itemAt(0);
22627 * Clears all selections.
22629 clearSelections : function(fast){
22634 var ds = this.grid.dataSource;
22635 var s = this.selections;
22636 s.each(function(r){
22637 this.deselectRow(ds.indexOfId(r.id));
22641 this.selections.clear();
22648 * Selects all rows.
22650 selectAll : function(){
22654 this.selections.clear();
22655 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22656 this.selectRow(i, true);
22661 * Returns True if there is a selection.
22662 * @return {Boolean}
22664 hasSelection : function(){
22665 return this.selections.length > 0;
22669 * Returns True if the specified row is selected.
22670 * @param {Number/Record} record The record or index of the record to check
22671 * @return {Boolean}
22673 isSelected : function(index){
22674 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22675 return (r && this.selections.key(r.id) ? true : false);
22679 * Returns True if the specified record id is selected.
22680 * @param {String} id The id of record to check
22681 * @return {Boolean}
22683 isIdSelected : function(id){
22684 return (this.selections.key(id) ? true : false);
22688 handleMouseDown : function(e, t){
22689 var view = this.grid.getView(), rowIndex;
22690 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22693 if(e.shiftKey && this.last !== false){
22694 var last = this.last;
22695 this.selectRange(last, rowIndex, e.ctrlKey);
22696 this.last = last; // reset the last
22697 view.focusRow(rowIndex);
22699 var isSelected = this.isSelected(rowIndex);
22700 if(e.button !== 0 && isSelected){
22701 view.focusRow(rowIndex);
22702 }else if(e.ctrlKey && isSelected){
22703 this.deselectRow(rowIndex);
22704 }else if(!isSelected){
22705 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22706 view.focusRow(rowIndex);
22709 this.fireEvent("afterselectionchange", this);
22712 handleDragableRowClick : function(grid, rowIndex, e)
22714 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22715 this.selectRow(rowIndex, false);
22716 grid.view.focusRow(rowIndex);
22717 this.fireEvent("afterselectionchange", this);
22722 * Selects multiple rows.
22723 * @param {Array} rows Array of the indexes of the row to select
22724 * @param {Boolean} keepExisting (optional) True to keep existing selections
22726 selectRows : function(rows, keepExisting){
22728 this.clearSelections();
22730 for(var i = 0, len = rows.length; i < len; i++){
22731 this.selectRow(rows[i], true);
22736 * Selects a range of rows. All rows in between startRow and endRow are also selected.
22737 * @param {Number} startRow The index of the first row in the range
22738 * @param {Number} endRow The index of the last row in the range
22739 * @param {Boolean} keepExisting (optional) True to retain existing selections
22741 selectRange : function(startRow, endRow, keepExisting){
22746 this.clearSelections();
22748 if(startRow <= endRow){
22749 for(var i = startRow; i <= endRow; i++){
22750 this.selectRow(i, true);
22753 for(var i = startRow; i >= endRow; i--){
22754 this.selectRow(i, true);
22760 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22761 * @param {Number} startRow The index of the first row in the range
22762 * @param {Number} endRow The index of the last row in the range
22764 deselectRange : function(startRow, endRow, preventViewNotify){
22768 for(var i = startRow; i <= endRow; i++){
22769 this.deselectRow(i, preventViewNotify);
22775 * @param {Number} row The index of the row to select
22776 * @param {Boolean} keepExisting (optional) True to keep existing selections
22778 selectRow : function(index, keepExisting, preventViewNotify){
22779 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22782 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22783 if(!keepExisting || this.singleSelect){
22784 this.clearSelections();
22786 var r = this.grid.dataSource.getAt(index);
22787 this.selections.add(r);
22788 this.last = this.lastActive = index;
22789 if(!preventViewNotify){
22790 this.grid.getView().onRowSelect(index);
22792 this.fireEvent("rowselect", this, index, r);
22793 this.fireEvent("selectionchange", this);
22799 * @param {Number} row The index of the row to deselect
22801 deselectRow : function(index, preventViewNotify){
22805 if(this.last == index){
22808 if(this.lastActive == index){
22809 this.lastActive = false;
22811 var r = this.grid.dataSource.getAt(index);
22812 this.selections.remove(r);
22813 if(!preventViewNotify){
22814 this.grid.getView().onRowDeselect(index);
22816 this.fireEvent("rowdeselect", this, index);
22817 this.fireEvent("selectionchange", this);
22821 restoreLast : function(){
22823 this.last = this._last;
22828 acceptsNav : function(row, col, cm){
22829 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22833 onEditorKey : function(field, e){
22834 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22839 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22841 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22843 }else if(k == e.ENTER && !e.ctrlKey){
22847 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22849 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22851 }else if(k == e.ESC){
22855 g.startEditing(newCell[0], newCell[1]);
22860 * Ext JS Library 1.1.1
22861 * Copyright(c) 2006-2007, Ext JS, LLC.
22863 * Originally Released Under LGPL - original licence link has changed is not relivant.
22866 * <script type="text/javascript">
22870 * @class Roo.bootstrap.PagingToolbar
22871 * @extends Roo.bootstrap.NavSimplebar
22872 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22874 * Create a new PagingToolbar
22875 * @param {Object} config The config object
22876 * @param {Roo.data.Store} store
22878 Roo.bootstrap.PagingToolbar = function(config)
22880 // old args format still supported... - xtype is prefered..
22881 // created from xtype...
22883 this.ds = config.dataSource;
22885 if (config.store && !this.ds) {
22886 this.store= Roo.factory(config.store, Roo.data);
22887 this.ds = this.store;
22888 this.ds.xmodule = this.xmodule || false;
22891 this.toolbarItems = [];
22892 if (config.items) {
22893 this.toolbarItems = config.items;
22896 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22901 this.bind(this.ds);
22904 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22908 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22910 * @cfg {Roo.data.Store} dataSource
22911 * The underlying data store providing the paged data
22914 * @cfg {String/HTMLElement/Element} container
22915 * container The id or element that will contain the toolbar
22918 * @cfg {Boolean} displayInfo
22919 * True to display the displayMsg (defaults to false)
22922 * @cfg {Number} pageSize
22923 * The number of records to display per page (defaults to 20)
22927 * @cfg {String} displayMsg
22928 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22930 displayMsg : 'Displaying {0} - {1} of {2}',
22932 * @cfg {String} emptyMsg
22933 * The message to display when no records are found (defaults to "No data to display")
22935 emptyMsg : 'No data to display',
22937 * Customizable piece of the default paging text (defaults to "Page")
22940 beforePageText : "Page",
22942 * Customizable piece of the default paging text (defaults to "of %0")
22945 afterPageText : "of {0}",
22947 * Customizable piece of the default paging text (defaults to "First Page")
22950 firstText : "First Page",
22952 * Customizable piece of the default paging text (defaults to "Previous Page")
22955 prevText : "Previous Page",
22957 * Customizable piece of the default paging text (defaults to "Next Page")
22960 nextText : "Next Page",
22962 * Customizable piece of the default paging text (defaults to "Last Page")
22965 lastText : "Last Page",
22967 * Customizable piece of the default paging text (defaults to "Refresh")
22970 refreshText : "Refresh",
22974 onRender : function(ct, position)
22976 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22977 this.navgroup.parentId = this.id;
22978 this.navgroup.onRender(this.el, null);
22979 // add the buttons to the navgroup
22981 if(this.displayInfo){
22982 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22983 this.displayEl = this.el.select('.x-paging-info', true).first();
22984 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22985 // this.displayEl = navel.el.select('span',true).first();
22991 Roo.each(_this.buttons, function(e){ // this might need to use render????
22992 Roo.factory(e).onRender(_this.el, null);
22996 Roo.each(_this.toolbarItems, function(e) {
22997 _this.navgroup.addItem(e);
23001 this.first = this.navgroup.addItem({
23002 tooltip: this.firstText,
23004 icon : 'fa fa-backward',
23006 preventDefault: true,
23007 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23010 this.prev = this.navgroup.addItem({
23011 tooltip: this.prevText,
23013 icon : 'fa fa-step-backward',
23015 preventDefault: true,
23016 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
23018 //this.addSeparator();
23021 var field = this.navgroup.addItem( {
23023 cls : 'x-paging-position',
23025 html : this.beforePageText +
23026 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23027 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
23030 this.field = field.el.select('input', true).first();
23031 this.field.on("keydown", this.onPagingKeydown, this);
23032 this.field.on("focus", function(){this.dom.select();});
23035 this.afterTextEl = field.el.select('.x-paging-after',true).first();
23036 //this.field.setHeight(18);
23037 //this.addSeparator();
23038 this.next = this.navgroup.addItem({
23039 tooltip: this.nextText,
23041 html : ' <i class="fa fa-step-forward">',
23043 preventDefault: true,
23044 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
23046 this.last = this.navgroup.addItem({
23047 tooltip: this.lastText,
23048 icon : 'fa fa-forward',
23051 preventDefault: true,
23052 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
23054 //this.addSeparator();
23055 this.loading = this.navgroup.addItem({
23056 tooltip: this.refreshText,
23057 icon: 'fa fa-refresh',
23058 preventDefault: true,
23059 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23065 updateInfo : function(){
23066 if(this.displayEl){
23067 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23068 var msg = count == 0 ?
23072 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
23074 this.displayEl.update(msg);
23079 onLoad : function(ds, r, o){
23080 this.cursor = o.params ? o.params.start : 0;
23081 var d = this.getPageData(),
23085 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23086 this.field.dom.value = ap;
23087 this.first.setDisabled(ap == 1);
23088 this.prev.setDisabled(ap == 1);
23089 this.next.setDisabled(ap == ps);
23090 this.last.setDisabled(ap == ps);
23091 this.loading.enable();
23096 getPageData : function(){
23097 var total = this.ds.getTotalCount();
23100 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23101 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23106 onLoadError : function(){
23107 this.loading.enable();
23111 onPagingKeydown : function(e){
23112 var k = e.getKey();
23113 var d = this.getPageData();
23115 var v = this.field.dom.value, pageNum;
23116 if(!v || isNaN(pageNum = parseInt(v, 10))){
23117 this.field.dom.value = d.activePage;
23120 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23121 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23124 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))
23126 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23127 this.field.dom.value = pageNum;
23128 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23131 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23133 var v = this.field.dom.value, pageNum;
23134 var increment = (e.shiftKey) ? 10 : 1;
23135 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23138 if(!v || isNaN(pageNum = parseInt(v, 10))) {
23139 this.field.dom.value = d.activePage;
23142 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23144 this.field.dom.value = parseInt(v, 10) + increment;
23145 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23146 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23153 beforeLoad : function(){
23155 this.loading.disable();
23160 onClick : function(which){
23169 ds.load({params:{start: 0, limit: this.pageSize}});
23172 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23175 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23178 var total = ds.getTotalCount();
23179 var extra = total % this.pageSize;
23180 var lastStart = extra ? (total - extra) : total-this.pageSize;
23181 ds.load({params:{start: lastStart, limit: this.pageSize}});
23184 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23190 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23191 * @param {Roo.data.Store} store The data store to unbind
23193 unbind : function(ds){
23194 ds.un("beforeload", this.beforeLoad, this);
23195 ds.un("load", this.onLoad, this);
23196 ds.un("loadexception", this.onLoadError, this);
23197 ds.un("remove", this.updateInfo, this);
23198 ds.un("add", this.updateInfo, this);
23199 this.ds = undefined;
23203 * Binds the paging toolbar to the specified {@link Roo.data.Store}
23204 * @param {Roo.data.Store} store The data store to bind
23206 bind : function(ds){
23207 ds.on("beforeload", this.beforeLoad, this);
23208 ds.on("load", this.onLoad, this);
23209 ds.on("loadexception", this.onLoadError, this);
23210 ds.on("remove", this.updateInfo, this);
23211 ds.on("add", this.updateInfo, this);
23222 * @class Roo.bootstrap.MessageBar
23223 * @extends Roo.bootstrap.Component
23224 * Bootstrap MessageBar class
23225 * @cfg {String} html contents of the MessageBar
23226 * @cfg {String} weight (info | success | warning | danger) default info
23227 * @cfg {String} beforeClass insert the bar before the given class
23228 * @cfg {Boolean} closable (true | false) default false
23229 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23232 * Create a new Element
23233 * @param {Object} config The config object
23236 Roo.bootstrap.MessageBar = function(config){
23237 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23240 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
23246 beforeClass: 'bootstrap-sticky-wrap',
23248 getAutoCreate : function(){
23252 cls: 'alert alert-dismissable alert-' + this.weight,
23257 html: this.html || ''
23263 cfg.cls += ' alert-messages-fixed';
23277 onRender : function(ct, position)
23279 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23282 var cfg = Roo.apply({}, this.getAutoCreate());
23286 cfg.cls += ' ' + this.cls;
23289 cfg.style = this.style;
23291 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23293 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23296 this.el.select('>button.close').on('click', this.hide, this);
23302 if (!this.rendered) {
23308 this.fireEvent('show', this);
23314 if (!this.rendered) {
23320 this.fireEvent('hide', this);
23323 update : function()
23325 // var e = this.el.dom.firstChild;
23327 // if(this.closable){
23328 // e = e.nextSibling;
23331 // e.data = this.html || '';
23333 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23349 * @class Roo.bootstrap.Graph
23350 * @extends Roo.bootstrap.Component
23351 * Bootstrap Graph class
23355 @cfg {String} graphtype bar | vbar | pie
23356 @cfg {number} g_x coodinator | centre x (pie)
23357 @cfg {number} g_y coodinator | centre y (pie)
23358 @cfg {number} g_r radius (pie)
23359 @cfg {number} g_height height of the chart (respected by all elements in the set)
23360 @cfg {number} g_width width of the chart (respected by all elements in the set)
23361 @cfg {Object} title The title of the chart
23364 -opts (object) options for the chart
23366 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23367 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23369 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.
23370 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23372 o stretch (boolean)
23374 -opts (object) options for the pie
23377 o startAngle (number)
23378 o endAngle (number)
23382 * Create a new Input
23383 * @param {Object} config The config object
23386 Roo.bootstrap.Graph = function(config){
23387 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23393 * The img click event for the img.
23394 * @param {Roo.EventObject} e
23400 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23411 //g_colors: this.colors,
23418 getAutoCreate : function(){
23429 onRender : function(ct,position){
23432 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23434 if (typeof(Raphael) == 'undefined') {
23435 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23439 this.raphael = Raphael(this.el.dom);
23441 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23442 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23443 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23444 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23446 r.text(160, 10, "Single Series Chart").attr(txtattr);
23447 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23448 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23449 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23451 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23452 r.barchart(330, 10, 300, 220, data1);
23453 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23454 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23457 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23458 // r.barchart(30, 30, 560, 250, xdata, {
23459 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23460 // axis : "0 0 1 1",
23461 // axisxlabels : xdata
23462 // //yvalues : cols,
23465 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23467 // this.load(null,xdata,{
23468 // axis : "0 0 1 1",
23469 // axisxlabels : xdata
23474 load : function(graphtype,xdata,opts)
23476 this.raphael.clear();
23478 graphtype = this.graphtype;
23483 var r = this.raphael,
23484 fin = function () {
23485 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23487 fout = function () {
23488 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23490 pfin = function() {
23491 this.sector.stop();
23492 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23495 this.label[0].stop();
23496 this.label[0].attr({ r: 7.5 });
23497 this.label[1].attr({ "font-weight": 800 });
23500 pfout = function() {
23501 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23504 this.label[0].animate({ r: 5 }, 500, "bounce");
23505 this.label[1].attr({ "font-weight": 400 });
23511 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23514 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23517 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23518 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23520 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23527 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23532 setTitle: function(o)
23537 initEvents: function() {
23540 this.el.on('click', this.onClick, this);
23544 onClick : function(e)
23546 Roo.log('img onclick');
23547 this.fireEvent('click', this, e);
23559 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23562 * @class Roo.bootstrap.dash.NumberBox
23563 * @extends Roo.bootstrap.Component
23564 * Bootstrap NumberBox class
23565 * @cfg {String} headline Box headline
23566 * @cfg {String} content Box content
23567 * @cfg {String} icon Box icon
23568 * @cfg {String} footer Footer text
23569 * @cfg {String} fhref Footer href
23572 * Create a new NumberBox
23573 * @param {Object} config The config object
23577 Roo.bootstrap.dash.NumberBox = function(config){
23578 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23582 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23591 getAutoCreate : function(){
23595 cls : 'small-box ',
23603 cls : 'roo-headline',
23604 html : this.headline
23608 cls : 'roo-content',
23609 html : this.content
23623 cls : 'ion ' + this.icon
23632 cls : 'small-box-footer',
23633 href : this.fhref || '#',
23637 cfg.cn.push(footer);
23644 onRender : function(ct,position){
23645 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23652 setHeadline: function (value)
23654 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23657 setFooter: function (value, href)
23659 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23662 this.el.select('a.small-box-footer',true).first().attr('href', href);
23667 setContent: function (value)
23669 this.el.select('.roo-content',true).first().dom.innerHTML = value;
23672 initEvents: function()
23686 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23689 * @class Roo.bootstrap.dash.TabBox
23690 * @extends Roo.bootstrap.Component
23691 * Bootstrap TabBox class
23692 * @cfg {String} title Title of the TabBox
23693 * @cfg {String} icon Icon of the TabBox
23694 * @cfg {Boolean} showtabs (true|false) show the tabs default true
23695 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23698 * Create a new TabBox
23699 * @param {Object} config The config object
23703 Roo.bootstrap.dash.TabBox = function(config){
23704 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23709 * When a pane is added
23710 * @param {Roo.bootstrap.dash.TabPane} pane
23714 * @event activatepane
23715 * When a pane is activated
23716 * @param {Roo.bootstrap.dash.TabPane} pane
23718 "activatepane" : true
23726 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
23731 tabScrollable : false,
23733 getChildContainer : function()
23735 return this.el.select('.tab-content', true).first();
23738 getAutoCreate : function(){
23742 cls: 'pull-left header',
23750 cls: 'fa ' + this.icon
23756 cls: 'nav nav-tabs pull-right',
23762 if(this.tabScrollable){
23769 cls: 'nav nav-tabs pull-right',
23780 cls: 'nav-tabs-custom',
23785 cls: 'tab-content no-padding',
23793 initEvents : function()
23795 //Roo.log('add add pane handler');
23796 this.on('addpane', this.onAddPane, this);
23799 * Updates the box title
23800 * @param {String} html to set the title to.
23802 setTitle : function(value)
23804 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23806 onAddPane : function(pane)
23808 this.panes.push(pane);
23809 //Roo.log('addpane');
23811 // tabs are rendere left to right..
23812 if(!this.showtabs){
23816 var ctr = this.el.select('.nav-tabs', true).first();
23819 var existing = ctr.select('.nav-tab',true);
23820 var qty = existing.getCount();;
23823 var tab = ctr.createChild({
23825 cls : 'nav-tab' + (qty ? '' : ' active'),
23833 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23836 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23838 pane.el.addClass('active');
23843 onTabClick : function(ev,un,ob,pane)
23845 //Roo.log('tab - prev default');
23846 ev.preventDefault();
23849 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23850 pane.tab.addClass('active');
23851 //Roo.log(pane.title);
23852 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23853 // technically we should have a deactivate event.. but maybe add later.
23854 // and it should not de-activate the selected tab...
23855 this.fireEvent('activatepane', pane);
23856 pane.el.addClass('active');
23857 pane.fireEvent('activate');
23862 getActivePane : function()
23865 Roo.each(this.panes, function(p) {
23866 if(p.el.hasClass('active')){
23887 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23889 * @class Roo.bootstrap.TabPane
23890 * @extends Roo.bootstrap.Component
23891 * Bootstrap TabPane class
23892 * @cfg {Boolean} active (false | true) Default false
23893 * @cfg {String} title title of panel
23897 * Create a new TabPane
23898 * @param {Object} config The config object
23901 Roo.bootstrap.dash.TabPane = function(config){
23902 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23908 * When a pane is activated
23909 * @param {Roo.bootstrap.dash.TabPane} pane
23916 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23921 // the tabBox that this is attached to.
23924 getAutoCreate : function()
23932 cfg.cls += ' active';
23937 initEvents : function()
23939 //Roo.log('trigger add pane handler');
23940 this.parent().fireEvent('addpane', this)
23944 * Updates the tab title
23945 * @param {String} html to set the title to.
23947 setTitle: function(str)
23953 this.tab.select('a', true).first().dom.innerHTML = str;
23970 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23973 * @class Roo.bootstrap.menu.Menu
23974 * @extends Roo.bootstrap.Component
23975 * Bootstrap Menu class - container for Menu
23976 * @cfg {String} html Text of the menu
23977 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23978 * @cfg {String} icon Font awesome icon
23979 * @cfg {String} pos Menu align to (top | bottom) default bottom
23983 * Create a new Menu
23984 * @param {Object} config The config object
23988 Roo.bootstrap.menu.Menu = function(config){
23989 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23993 * @event beforeshow
23994 * Fires before this menu is displayed
23995 * @param {Roo.bootstrap.menu.Menu} this
23999 * @event beforehide
24000 * Fires before this menu is hidden
24001 * @param {Roo.bootstrap.menu.Menu} this
24006 * Fires after this menu is displayed
24007 * @param {Roo.bootstrap.menu.Menu} this
24012 * Fires after this menu is hidden
24013 * @param {Roo.bootstrap.menu.Menu} this
24018 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24019 * @param {Roo.bootstrap.menu.Menu} this
24020 * @param {Roo.EventObject} e
24027 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
24031 weight : 'default',
24036 getChildContainer : function() {
24037 if(this.isSubMenu){
24041 return this.el.select('ul.dropdown-menu', true).first();
24044 getAutoCreate : function()
24049 cls : 'roo-menu-text',
24057 cls : 'fa ' + this.icon
24068 cls : 'dropdown-button btn btn-' + this.weight,
24073 cls : 'dropdown-toggle btn btn-' + this.weight,
24083 cls : 'dropdown-menu'
24089 if(this.pos == 'top'){
24090 cfg.cls += ' dropup';
24093 if(this.isSubMenu){
24096 cls : 'dropdown-menu'
24103 onRender : function(ct, position)
24105 this.isSubMenu = ct.hasClass('dropdown-submenu');
24107 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24110 initEvents : function()
24112 if(this.isSubMenu){
24116 this.hidden = true;
24118 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24119 this.triggerEl.on('click', this.onTriggerPress, this);
24121 this.buttonEl = this.el.select('button.dropdown-button', true).first();
24122 this.buttonEl.on('click', this.onClick, this);
24128 if(this.isSubMenu){
24132 return this.el.select('ul.dropdown-menu', true).first();
24135 onClick : function(e)
24137 this.fireEvent("click", this, e);
24140 onTriggerPress : function(e)
24142 if (this.isVisible()) {
24149 isVisible : function(){
24150 return !this.hidden;
24155 this.fireEvent("beforeshow", this);
24157 this.hidden = false;
24158 this.el.addClass('open');
24160 Roo.get(document).on("mouseup", this.onMouseUp, this);
24162 this.fireEvent("show", this);
24169 this.fireEvent("beforehide", this);
24171 this.hidden = true;
24172 this.el.removeClass('open');
24174 Roo.get(document).un("mouseup", this.onMouseUp);
24176 this.fireEvent("hide", this);
24179 onMouseUp : function()
24193 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24196 * @class Roo.bootstrap.menu.Item
24197 * @extends Roo.bootstrap.Component
24198 * Bootstrap MenuItem class
24199 * @cfg {Boolean} submenu (true | false) default false
24200 * @cfg {String} html text of the item
24201 * @cfg {String} href the link
24202 * @cfg {Boolean} disable (true | false) default false
24203 * @cfg {Boolean} preventDefault (true | false) default true
24204 * @cfg {String} icon Font awesome icon
24205 * @cfg {String} pos Submenu align to (left | right) default right
24209 * Create a new Item
24210 * @param {Object} config The config object
24214 Roo.bootstrap.menu.Item = function(config){
24215 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24219 * Fires when the mouse is hovering over this menu
24220 * @param {Roo.bootstrap.menu.Item} this
24221 * @param {Roo.EventObject} e
24226 * Fires when the mouse exits this menu
24227 * @param {Roo.bootstrap.menu.Item} this
24228 * @param {Roo.EventObject} e
24234 * The raw click event for the entire grid.
24235 * @param {Roo.EventObject} e
24241 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
24246 preventDefault: true,
24251 getAutoCreate : function()
24256 cls : 'roo-menu-item-text',
24264 cls : 'fa ' + this.icon
24273 href : this.href || '#',
24280 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24284 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24286 if(this.pos == 'left'){
24287 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24294 initEvents : function()
24296 this.el.on('mouseover', this.onMouseOver, this);
24297 this.el.on('mouseout', this.onMouseOut, this);
24299 this.el.select('a', true).first().on('click', this.onClick, this);
24303 onClick : function(e)
24305 if(this.preventDefault){
24306 e.preventDefault();
24309 this.fireEvent("click", this, e);
24312 onMouseOver : function(e)
24314 if(this.submenu && this.pos == 'left'){
24315 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24318 this.fireEvent("mouseover", this, e);
24321 onMouseOut : function(e)
24323 this.fireEvent("mouseout", this, e);
24335 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24338 * @class Roo.bootstrap.menu.Separator
24339 * @extends Roo.bootstrap.Component
24340 * Bootstrap Separator class
24343 * Create a new Separator
24344 * @param {Object} config The config object
24348 Roo.bootstrap.menu.Separator = function(config){
24349 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24352 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24354 getAutoCreate : function(){
24375 * @class Roo.bootstrap.Tooltip
24376 * Bootstrap Tooltip class
24377 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24378 * to determine which dom element triggers the tooltip.
24380 * It needs to add support for additional attributes like tooltip-position
24383 * Create a new Toolti
24384 * @param {Object} config The config object
24387 Roo.bootstrap.Tooltip = function(config){
24388 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24391 Roo.apply(Roo.bootstrap.Tooltip, {
24393 * @function init initialize tooltip monitoring.
24397 currentTip : false,
24398 currentRegion : false,
24404 Roo.get(document).on('mouseover', this.enter ,this);
24405 Roo.get(document).on('mouseout', this.leave, this);
24408 this.currentTip = new Roo.bootstrap.Tooltip();
24411 enter : function(ev)
24413 var dom = ev.getTarget();
24415 //Roo.log(['enter',dom]);
24416 var el = Roo.fly(dom);
24417 if (this.currentEl) {
24419 //Roo.log(this.currentEl);
24420 //Roo.log(this.currentEl.contains(dom));
24421 if (this.currentEl == el) {
24424 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24430 if (this.currentTip.el) {
24431 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24435 if(!el || el.dom == document){
24441 // you can not look for children, as if el is the body.. then everythign is the child..
24442 if (!el.attr('tooltip')) { //
24443 if (!el.select("[tooltip]").elements.length) {
24446 // is the mouse over this child...?
24447 bindEl = el.select("[tooltip]").first();
24448 var xy = ev.getXY();
24449 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24450 //Roo.log("not in region.");
24453 //Roo.log("child element over..");
24456 this.currentEl = bindEl;
24457 this.currentTip.bind(bindEl);
24458 this.currentRegion = Roo.lib.Region.getRegion(dom);
24459 this.currentTip.enter();
24462 leave : function(ev)
24464 var dom = ev.getTarget();
24465 //Roo.log(['leave',dom]);
24466 if (!this.currentEl) {
24471 if (dom != this.currentEl.dom) {
24474 var xy = ev.getXY();
24475 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24478 // only activate leave if mouse cursor is outside... bounding box..
24483 if (this.currentTip) {
24484 this.currentTip.leave();
24486 //Roo.log('clear currentEl');
24487 this.currentEl = false;
24492 'left' : ['r-l', [-2,0], 'right'],
24493 'right' : ['l-r', [2,0], 'left'],
24494 'bottom' : ['t-b', [0,2], 'top'],
24495 'top' : [ 'b-t', [0,-2], 'bottom']
24501 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24506 delay : null, // can be { show : 300 , hide: 500}
24510 hoverState : null, //???
24512 placement : 'bottom',
24514 getAutoCreate : function(){
24521 cls : 'tooltip-arrow'
24524 cls : 'tooltip-inner'
24531 bind : function(el)
24537 enter : function () {
24539 if (this.timeout != null) {
24540 clearTimeout(this.timeout);
24543 this.hoverState = 'in';
24544 //Roo.log("enter - show");
24545 if (!this.delay || !this.delay.show) {
24550 this.timeout = setTimeout(function () {
24551 if (_t.hoverState == 'in') {
24554 }, this.delay.show);
24558 clearTimeout(this.timeout);
24560 this.hoverState = 'out';
24561 if (!this.delay || !this.delay.hide) {
24567 this.timeout = setTimeout(function () {
24568 //Roo.log("leave - timeout");
24570 if (_t.hoverState == 'out') {
24572 Roo.bootstrap.Tooltip.currentEl = false;
24580 this.render(document.body);
24583 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24585 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24587 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24589 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24591 var placement = typeof this.placement == 'function' ?
24592 this.placement.call(this, this.el, on_el) :
24595 var autoToken = /\s?auto?\s?/i;
24596 var autoPlace = autoToken.test(placement);
24598 placement = placement.replace(autoToken, '') || 'top';
24602 //this.el.setXY([0,0]);
24604 //this.el.dom.style.display='block';
24606 //this.el.appendTo(on_el);
24608 var p = this.getPosition();
24609 var box = this.el.getBox();
24615 var align = Roo.bootstrap.Tooltip.alignment[placement];
24617 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24619 if(placement == 'top' || placement == 'bottom'){
24621 placement = 'right';
24624 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24625 placement = 'left';
24628 var scroll = Roo.select('body', true).first().getScroll();
24630 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24636 align = Roo.bootstrap.Tooltip.alignment[placement];
24638 this.el.alignTo(this.bindEl, align[0],align[1]);
24639 //var arrow = this.el.select('.arrow',true).first();
24640 //arrow.set(align[2],
24642 this.el.addClass(placement);
24644 this.el.addClass('in fade');
24646 this.hoverState = null;
24648 if (this.el.hasClass('fade')) {
24659 //this.el.setXY([0,0]);
24660 this.el.removeClass('in');
24676 * @class Roo.bootstrap.LocationPicker
24677 * @extends Roo.bootstrap.Component
24678 * Bootstrap LocationPicker class
24679 * @cfg {Number} latitude Position when init default 0
24680 * @cfg {Number} longitude Position when init default 0
24681 * @cfg {Number} zoom default 15
24682 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24683 * @cfg {Boolean} mapTypeControl default false
24684 * @cfg {Boolean} disableDoubleClickZoom default false
24685 * @cfg {Boolean} scrollwheel default true
24686 * @cfg {Boolean} streetViewControl default false
24687 * @cfg {Number} radius default 0
24688 * @cfg {String} locationName
24689 * @cfg {Boolean} draggable default true
24690 * @cfg {Boolean} enableAutocomplete default false
24691 * @cfg {Boolean} enableReverseGeocode default true
24692 * @cfg {String} markerTitle
24695 * Create a new LocationPicker
24696 * @param {Object} config The config object
24700 Roo.bootstrap.LocationPicker = function(config){
24702 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24707 * Fires when the picker initialized.
24708 * @param {Roo.bootstrap.LocationPicker} this
24709 * @param {Google Location} location
24713 * @event positionchanged
24714 * Fires when the picker position changed.
24715 * @param {Roo.bootstrap.LocationPicker} this
24716 * @param {Google Location} location
24718 positionchanged : true,
24721 * Fires when the map resize.
24722 * @param {Roo.bootstrap.LocationPicker} this
24727 * Fires when the map show.
24728 * @param {Roo.bootstrap.LocationPicker} this
24733 * Fires when the map hide.
24734 * @param {Roo.bootstrap.LocationPicker} this
24739 * Fires when click the map.
24740 * @param {Roo.bootstrap.LocationPicker} this
24741 * @param {Map event} e
24745 * @event mapRightClick
24746 * Fires when right click the map.
24747 * @param {Roo.bootstrap.LocationPicker} this
24748 * @param {Map event} e
24750 mapRightClick : true,
24752 * @event markerClick
24753 * Fires when click the marker.
24754 * @param {Roo.bootstrap.LocationPicker} this
24755 * @param {Map event} e
24757 markerClick : true,
24759 * @event markerRightClick
24760 * Fires when right click the marker.
24761 * @param {Roo.bootstrap.LocationPicker} this
24762 * @param {Map event} e
24764 markerRightClick : true,
24766 * @event OverlayViewDraw
24767 * Fires when OverlayView Draw
24768 * @param {Roo.bootstrap.LocationPicker} this
24770 OverlayViewDraw : true,
24772 * @event OverlayViewOnAdd
24773 * Fires when OverlayView Draw
24774 * @param {Roo.bootstrap.LocationPicker} this
24776 OverlayViewOnAdd : true,
24778 * @event OverlayViewOnRemove
24779 * Fires when OverlayView Draw
24780 * @param {Roo.bootstrap.LocationPicker} this
24782 OverlayViewOnRemove : true,
24784 * @event OverlayViewShow
24785 * Fires when OverlayView Draw
24786 * @param {Roo.bootstrap.LocationPicker} this
24787 * @param {Pixel} cpx
24789 OverlayViewShow : true,
24791 * @event OverlayViewHide
24792 * Fires when OverlayView Draw
24793 * @param {Roo.bootstrap.LocationPicker} this
24795 OverlayViewHide : true,
24797 * @event loadexception
24798 * Fires when load google lib failed.
24799 * @param {Roo.bootstrap.LocationPicker} this
24801 loadexception : true
24806 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24808 gMapContext: false,
24814 mapTypeControl: false,
24815 disableDoubleClickZoom: false,
24817 streetViewControl: false,
24821 enableAutocomplete: false,
24822 enableReverseGeocode: true,
24825 getAutoCreate: function()
24830 cls: 'roo-location-picker'
24836 initEvents: function(ct, position)
24838 if(!this.el.getWidth() || this.isApplied()){
24842 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24847 initial: function()
24849 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24850 this.fireEvent('loadexception', this);
24854 if(!this.mapTypeId){
24855 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24858 this.gMapContext = this.GMapContext();
24860 this.initOverlayView();
24862 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24866 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24867 _this.setPosition(_this.gMapContext.marker.position);
24870 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24871 _this.fireEvent('mapClick', this, event);
24875 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24876 _this.fireEvent('mapRightClick', this, event);
24880 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24881 _this.fireEvent('markerClick', this, event);
24885 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24886 _this.fireEvent('markerRightClick', this, event);
24890 this.setPosition(this.gMapContext.location);
24892 this.fireEvent('initial', this, this.gMapContext.location);
24895 initOverlayView: function()
24899 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24903 _this.fireEvent('OverlayViewDraw', _this);
24908 _this.fireEvent('OverlayViewOnAdd', _this);
24911 onRemove: function()
24913 _this.fireEvent('OverlayViewOnRemove', _this);
24916 show: function(cpx)
24918 _this.fireEvent('OverlayViewShow', _this, cpx);
24923 _this.fireEvent('OverlayViewHide', _this);
24929 fromLatLngToContainerPixel: function(event)
24931 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24934 isApplied: function()
24936 return this.getGmapContext() == false ? false : true;
24939 getGmapContext: function()
24941 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24944 GMapContext: function()
24946 var position = new google.maps.LatLng(this.latitude, this.longitude);
24948 var _map = new google.maps.Map(this.el.dom, {
24951 mapTypeId: this.mapTypeId,
24952 mapTypeControl: this.mapTypeControl,
24953 disableDoubleClickZoom: this.disableDoubleClickZoom,
24954 scrollwheel: this.scrollwheel,
24955 streetViewControl: this.streetViewControl,
24956 locationName: this.locationName,
24957 draggable: this.draggable,
24958 enableAutocomplete: this.enableAutocomplete,
24959 enableReverseGeocode: this.enableReverseGeocode
24962 var _marker = new google.maps.Marker({
24963 position: position,
24965 title: this.markerTitle,
24966 draggable: this.draggable
24973 location: position,
24974 radius: this.radius,
24975 locationName: this.locationName,
24976 addressComponents: {
24977 formatted_address: null,
24978 addressLine1: null,
24979 addressLine2: null,
24981 streetNumber: null,
24985 stateOrProvince: null
24988 domContainer: this.el.dom,
24989 geodecoder: new google.maps.Geocoder()
24993 drawCircle: function(center, radius, options)
24995 if (this.gMapContext.circle != null) {
24996 this.gMapContext.circle.setMap(null);
25000 options = Roo.apply({}, options, {
25001 strokeColor: "#0000FF",
25002 strokeOpacity: .35,
25004 fillColor: "#0000FF",
25008 options.map = this.gMapContext.map;
25009 options.radius = radius;
25010 options.center = center;
25011 this.gMapContext.circle = new google.maps.Circle(options);
25012 return this.gMapContext.circle;
25018 setPosition: function(location)
25020 this.gMapContext.location = location;
25021 this.gMapContext.marker.setPosition(location);
25022 this.gMapContext.map.panTo(location);
25023 this.drawCircle(location, this.gMapContext.radius, {});
25027 if (this.gMapContext.settings.enableReverseGeocode) {
25028 this.gMapContext.geodecoder.geocode({
25029 latLng: this.gMapContext.location
25030 }, function(results, status) {
25032 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25033 _this.gMapContext.locationName = results[0].formatted_address;
25034 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25036 _this.fireEvent('positionchanged', this, location);
25043 this.fireEvent('positionchanged', this, location);
25048 google.maps.event.trigger(this.gMapContext.map, "resize");
25050 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25052 this.fireEvent('resize', this);
25055 setPositionByLatLng: function(latitude, longitude)
25057 this.setPosition(new google.maps.LatLng(latitude, longitude));
25060 getCurrentPosition: function()
25063 latitude: this.gMapContext.location.lat(),
25064 longitude: this.gMapContext.location.lng()
25068 getAddressName: function()
25070 return this.gMapContext.locationName;
25073 getAddressComponents: function()
25075 return this.gMapContext.addressComponents;
25078 address_component_from_google_geocode: function(address_components)
25082 for (var i = 0; i < address_components.length; i++) {
25083 var component = address_components[i];
25084 if (component.types.indexOf("postal_code") >= 0) {
25085 result.postalCode = component.short_name;
25086 } else if (component.types.indexOf("street_number") >= 0) {
25087 result.streetNumber = component.short_name;
25088 } else if (component.types.indexOf("route") >= 0) {
25089 result.streetName = component.short_name;
25090 } else if (component.types.indexOf("neighborhood") >= 0) {
25091 result.city = component.short_name;
25092 } else if (component.types.indexOf("locality") >= 0) {
25093 result.city = component.short_name;
25094 } else if (component.types.indexOf("sublocality") >= 0) {
25095 result.district = component.short_name;
25096 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25097 result.stateOrProvince = component.short_name;
25098 } else if (component.types.indexOf("country") >= 0) {
25099 result.country = component.short_name;
25103 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25104 result.addressLine2 = "";
25108 setZoomLevel: function(zoom)
25110 this.gMapContext.map.setZoom(zoom);
25123 this.fireEvent('show', this);
25134 this.fireEvent('hide', this);
25139 Roo.apply(Roo.bootstrap.LocationPicker, {
25141 OverlayView : function(map, options)
25143 options = options || {};
25157 * @class Roo.bootstrap.Alert
25158 * @extends Roo.bootstrap.Component
25159 * Bootstrap Alert class
25160 * @cfg {String} title The title of alert
25161 * @cfg {String} html The content of alert
25162 * @cfg {String} weight ( success | info | warning | danger )
25163 * @cfg {String} faicon font-awesomeicon
25166 * Create a new alert
25167 * @param {Object} config The config object
25171 Roo.bootstrap.Alert = function(config){
25172 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25176 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
25183 getAutoCreate : function()
25192 cls : 'roo-alert-icon'
25197 cls : 'roo-alert-title',
25202 cls : 'roo-alert-text',
25209 cfg.cn[0].cls += ' fa ' + this.faicon;
25213 cfg.cls += ' alert-' + this.weight;
25219 initEvents: function()
25221 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25224 setTitle : function(str)
25226 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25229 setText : function(str)
25231 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25234 setWeight : function(weight)
25237 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25240 this.weight = weight;
25242 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25245 setIcon : function(icon)
25248 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25251 this.faicon = icon;
25253 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25274 * @class Roo.bootstrap.UploadCropbox
25275 * @extends Roo.bootstrap.Component
25276 * Bootstrap UploadCropbox class
25277 * @cfg {String} emptyText show when image has been loaded
25278 * @cfg {String} rotateNotify show when image too small to rotate
25279 * @cfg {Number} errorTimeout default 3000
25280 * @cfg {Number} minWidth default 300
25281 * @cfg {Number} minHeight default 300
25282 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25283 * @cfg {Boolean} isDocument (true|false) default false
25284 * @cfg {String} url action url
25285 * @cfg {String} paramName default 'imageUpload'
25286 * @cfg {String} method default POST
25287 * @cfg {Boolean} loadMask (true|false) default true
25288 * @cfg {Boolean} loadingText default 'Loading...'
25291 * Create a new UploadCropbox
25292 * @param {Object} config The config object
25295 Roo.bootstrap.UploadCropbox = function(config){
25296 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25300 * @event beforeselectfile
25301 * Fire before select file
25302 * @param {Roo.bootstrap.UploadCropbox} this
25304 "beforeselectfile" : true,
25307 * Fire after initEvent
25308 * @param {Roo.bootstrap.UploadCropbox} this
25313 * Fire after initEvent
25314 * @param {Roo.bootstrap.UploadCropbox} this
25315 * @param {String} data
25320 * Fire when preparing the file data
25321 * @param {Roo.bootstrap.UploadCropbox} this
25322 * @param {Object} file
25327 * Fire when get exception
25328 * @param {Roo.bootstrap.UploadCropbox} this
25329 * @param {XMLHttpRequest} xhr
25331 "exception" : true,
25333 * @event beforeloadcanvas
25334 * Fire before load the canvas
25335 * @param {Roo.bootstrap.UploadCropbox} this
25336 * @param {String} src
25338 "beforeloadcanvas" : true,
25341 * Fire when trash image
25342 * @param {Roo.bootstrap.UploadCropbox} this
25347 * Fire when download the image
25348 * @param {Roo.bootstrap.UploadCropbox} this
25352 * @event footerbuttonclick
25353 * Fire when footerbuttonclick
25354 * @param {Roo.bootstrap.UploadCropbox} this
25355 * @param {String} type
25357 "footerbuttonclick" : true,
25361 * @param {Roo.bootstrap.UploadCropbox} this
25366 * Fire when rotate the image
25367 * @param {Roo.bootstrap.UploadCropbox} this
25368 * @param {String} pos
25373 * Fire when inspect the file
25374 * @param {Roo.bootstrap.UploadCropbox} this
25375 * @param {Object} file
25380 * Fire when xhr upload the file
25381 * @param {Roo.bootstrap.UploadCropbox} this
25382 * @param {Object} data
25387 * Fire when arrange the file data
25388 * @param {Roo.bootstrap.UploadCropbox} this
25389 * @param {Object} formData
25394 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25397 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25399 emptyText : 'Click to upload image',
25400 rotateNotify : 'Image is too small to rotate',
25401 errorTimeout : 3000,
25415 cropType : 'image/jpeg',
25417 canvasLoaded : false,
25418 isDocument : false,
25420 paramName : 'imageUpload',
25422 loadingText : 'Loading...',
25425 getAutoCreate : function()
25429 cls : 'roo-upload-cropbox',
25433 cls : 'roo-upload-cropbox-selector',
25438 cls : 'roo-upload-cropbox-body',
25439 style : 'cursor:pointer',
25443 cls : 'roo-upload-cropbox-preview'
25447 cls : 'roo-upload-cropbox-thumb'
25451 cls : 'roo-upload-cropbox-empty-notify',
25452 html : this.emptyText
25456 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25457 html : this.rotateNotify
25463 cls : 'roo-upload-cropbox-footer',
25466 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25476 onRender : function(ct, position)
25478 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25480 if (this.buttons.length) {
25482 Roo.each(this.buttons, function(bb) {
25484 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25486 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25492 this.maskEl = this.el;
25496 initEvents : function()
25498 this.urlAPI = (window.createObjectURL && window) ||
25499 (window.URL && URL.revokeObjectURL && URL) ||
25500 (window.webkitURL && webkitURL);
25502 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25503 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25505 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25506 this.selectorEl.hide();
25508 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25509 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25511 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25512 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25513 this.thumbEl.hide();
25515 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25516 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25518 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25519 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25520 this.errorEl.hide();
25522 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25523 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25524 this.footerEl.hide();
25526 this.setThumbBoxSize();
25532 this.fireEvent('initial', this);
25539 window.addEventListener("resize", function() { _this.resize(); } );
25541 this.bodyEl.on('click', this.beforeSelectFile, this);
25544 this.bodyEl.on('touchstart', this.onTouchStart, this);
25545 this.bodyEl.on('touchmove', this.onTouchMove, this);
25546 this.bodyEl.on('touchend', this.onTouchEnd, this);
25550 this.bodyEl.on('mousedown', this.onMouseDown, this);
25551 this.bodyEl.on('mousemove', this.onMouseMove, this);
25552 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25553 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25554 Roo.get(document).on('mouseup', this.onMouseUp, this);
25557 this.selectorEl.on('change', this.onFileSelected, this);
25563 this.baseScale = 1;
25565 this.baseRotate = 1;
25566 this.dragable = false;
25567 this.pinching = false;
25570 this.cropData = false;
25571 this.notifyEl.dom.innerHTML = this.emptyText;
25573 this.selectorEl.dom.value = '';
25577 resize : function()
25579 if(this.fireEvent('resize', this) != false){
25580 this.setThumbBoxPosition();
25581 this.setCanvasPosition();
25585 onFooterButtonClick : function(e, el, o, type)
25588 case 'rotate-left' :
25589 this.onRotateLeft(e);
25591 case 'rotate-right' :
25592 this.onRotateRight(e);
25595 this.beforeSelectFile(e);
25610 this.fireEvent('footerbuttonclick', this, type);
25613 beforeSelectFile : function(e)
25615 e.preventDefault();
25617 if(this.fireEvent('beforeselectfile', this) != false){
25618 this.selectorEl.dom.click();
25622 onFileSelected : function(e)
25624 e.preventDefault();
25626 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25630 var file = this.selectorEl.dom.files[0];
25632 if(this.fireEvent('inspect', this, file) != false){
25633 this.prepare(file);
25638 trash : function(e)
25640 this.fireEvent('trash', this);
25643 download : function(e)
25645 this.fireEvent('download', this);
25648 loadCanvas : function(src)
25650 if(this.fireEvent('beforeloadcanvas', this, src) != false){
25654 this.imageEl = document.createElement('img');
25658 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25660 this.imageEl.src = src;
25664 onLoadCanvas : function()
25666 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25667 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25669 this.bodyEl.un('click', this.beforeSelectFile, this);
25671 this.notifyEl.hide();
25672 this.thumbEl.show();
25673 this.footerEl.show();
25675 this.baseRotateLevel();
25677 if(this.isDocument){
25678 this.setThumbBoxSize();
25681 this.setThumbBoxPosition();
25683 this.baseScaleLevel();
25689 this.canvasLoaded = true;
25692 this.maskEl.unmask();
25697 setCanvasPosition : function()
25699 if(!this.canvasEl){
25703 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25704 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25706 this.previewEl.setLeft(pw);
25707 this.previewEl.setTop(ph);
25711 onMouseDown : function(e)
25715 this.dragable = true;
25716 this.pinching = false;
25718 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25719 this.dragable = false;
25723 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25724 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25728 onMouseMove : function(e)
25732 if(!this.canvasLoaded){
25736 if (!this.dragable){
25740 var minX = Math.ceil(this.thumbEl.getLeft(true));
25741 var minY = Math.ceil(this.thumbEl.getTop(true));
25743 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25744 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25746 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25747 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25749 x = x - this.mouseX;
25750 y = y - this.mouseY;
25752 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25753 var bgY = Math.ceil(y + this.previewEl.getTop(true));
25755 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25756 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25758 this.previewEl.setLeft(bgX);
25759 this.previewEl.setTop(bgY);
25761 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25762 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25765 onMouseUp : function(e)
25769 this.dragable = false;
25772 onMouseWheel : function(e)
25776 this.startScale = this.scale;
25778 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25780 if(!this.zoomable()){
25781 this.scale = this.startScale;
25790 zoomable : function()
25792 var minScale = this.thumbEl.getWidth() / this.minWidth;
25794 if(this.minWidth < this.minHeight){
25795 minScale = this.thumbEl.getHeight() / this.minHeight;
25798 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25799 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25803 (this.rotate == 0 || this.rotate == 180) &&
25805 width > this.imageEl.OriginWidth ||
25806 height > this.imageEl.OriginHeight ||
25807 (width < this.minWidth && height < this.minHeight)
25815 (this.rotate == 90 || this.rotate == 270) &&
25817 width > this.imageEl.OriginWidth ||
25818 height > this.imageEl.OriginHeight ||
25819 (width < this.minHeight && height < this.minWidth)
25826 !this.isDocument &&
25827 (this.rotate == 0 || this.rotate == 180) &&
25829 width < this.minWidth ||
25830 width > this.imageEl.OriginWidth ||
25831 height < this.minHeight ||
25832 height > this.imageEl.OriginHeight
25839 !this.isDocument &&
25840 (this.rotate == 90 || this.rotate == 270) &&
25842 width < this.minHeight ||
25843 width > this.imageEl.OriginWidth ||
25844 height < this.minWidth ||
25845 height > this.imageEl.OriginHeight
25855 onRotateLeft : function(e)
25857 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25859 var minScale = this.thumbEl.getWidth() / this.minWidth;
25861 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25862 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25864 this.startScale = this.scale;
25866 while (this.getScaleLevel() < minScale){
25868 this.scale = this.scale + 1;
25870 if(!this.zoomable()){
25875 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25876 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25881 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25888 this.scale = this.startScale;
25890 this.onRotateFail();
25895 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25897 if(this.isDocument){
25898 this.setThumbBoxSize();
25899 this.setThumbBoxPosition();
25900 this.setCanvasPosition();
25905 this.fireEvent('rotate', this, 'left');
25909 onRotateRight : function(e)
25911 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25913 var minScale = this.thumbEl.getWidth() / this.minWidth;
25915 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25916 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25918 this.startScale = this.scale;
25920 while (this.getScaleLevel() < minScale){
25922 this.scale = this.scale + 1;
25924 if(!this.zoomable()){
25929 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25930 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25935 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25942 this.scale = this.startScale;
25944 this.onRotateFail();
25949 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25951 if(this.isDocument){
25952 this.setThumbBoxSize();
25953 this.setThumbBoxPosition();
25954 this.setCanvasPosition();
25959 this.fireEvent('rotate', this, 'right');
25962 onRotateFail : function()
25964 this.errorEl.show(true);
25968 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25973 this.previewEl.dom.innerHTML = '';
25975 var canvasEl = document.createElement("canvas");
25977 var contextEl = canvasEl.getContext("2d");
25979 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25980 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25981 var center = this.imageEl.OriginWidth / 2;
25983 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25984 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25985 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25986 center = this.imageEl.OriginHeight / 2;
25989 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25991 contextEl.translate(center, center);
25992 contextEl.rotate(this.rotate * Math.PI / 180);
25994 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25996 this.canvasEl = document.createElement("canvas");
25998 this.contextEl = this.canvasEl.getContext("2d");
26000 switch (this.rotate) {
26003 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26004 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26006 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26011 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26012 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26014 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26015 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);
26019 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26024 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26025 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26027 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26028 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);
26032 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);
26037 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26038 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26040 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26041 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26045 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);
26052 this.previewEl.appendChild(this.canvasEl);
26054 this.setCanvasPosition();
26059 if(!this.canvasLoaded){
26063 var imageCanvas = document.createElement("canvas");
26065 var imageContext = imageCanvas.getContext("2d");
26067 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26068 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26070 var center = imageCanvas.width / 2;
26072 imageContext.translate(center, center);
26074 imageContext.rotate(this.rotate * Math.PI / 180);
26076 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26078 var canvas = document.createElement("canvas");
26080 var context = canvas.getContext("2d");
26082 canvas.width = this.minWidth;
26083 canvas.height = this.minHeight;
26085 switch (this.rotate) {
26088 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26089 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26091 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26092 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26094 var targetWidth = this.minWidth - 2 * x;
26095 var targetHeight = this.minHeight - 2 * y;
26099 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26100 scale = targetWidth / width;
26103 if(x > 0 && y == 0){
26104 scale = targetHeight / height;
26107 if(x > 0 && y > 0){
26108 scale = targetWidth / width;
26110 if(width < height){
26111 scale = targetHeight / height;
26115 context.scale(scale, scale);
26117 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26118 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26120 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26121 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26123 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26128 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26129 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26131 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26132 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26134 var targetWidth = this.minWidth - 2 * x;
26135 var targetHeight = this.minHeight - 2 * y;
26139 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26140 scale = targetWidth / width;
26143 if(x > 0 && y == 0){
26144 scale = targetHeight / height;
26147 if(x > 0 && y > 0){
26148 scale = targetWidth / width;
26150 if(width < height){
26151 scale = targetHeight / height;
26155 context.scale(scale, scale);
26157 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26158 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26160 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26161 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26163 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26165 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26170 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26171 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26173 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26174 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26176 var targetWidth = this.minWidth - 2 * x;
26177 var targetHeight = this.minHeight - 2 * y;
26181 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26182 scale = targetWidth / width;
26185 if(x > 0 && y == 0){
26186 scale = targetHeight / height;
26189 if(x > 0 && y > 0){
26190 scale = targetWidth / width;
26192 if(width < height){
26193 scale = targetHeight / height;
26197 context.scale(scale, scale);
26199 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26200 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26202 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26203 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26205 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26206 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26208 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26213 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26214 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26216 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26217 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26219 var targetWidth = this.minWidth - 2 * x;
26220 var targetHeight = this.minHeight - 2 * y;
26224 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26225 scale = targetWidth / width;
26228 if(x > 0 && y == 0){
26229 scale = targetHeight / height;
26232 if(x > 0 && y > 0){
26233 scale = targetWidth / width;
26235 if(width < height){
26236 scale = targetHeight / height;
26240 context.scale(scale, scale);
26242 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26243 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26245 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26246 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26248 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26250 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26257 this.cropData = canvas.toDataURL(this.cropType);
26259 if(this.fireEvent('crop', this, this.cropData) !== false){
26260 this.process(this.file, this.cropData);
26267 setThumbBoxSize : function()
26271 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26272 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26273 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26275 this.minWidth = width;
26276 this.minHeight = height;
26278 if(this.rotate == 90 || this.rotate == 270){
26279 this.minWidth = height;
26280 this.minHeight = width;
26285 width = Math.ceil(this.minWidth * height / this.minHeight);
26287 if(this.minWidth > this.minHeight){
26289 height = Math.ceil(this.minHeight * width / this.minWidth);
26292 this.thumbEl.setStyle({
26293 width : width + 'px',
26294 height : height + 'px'
26301 setThumbBoxPosition : function()
26303 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26304 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26306 this.thumbEl.setLeft(x);
26307 this.thumbEl.setTop(y);
26311 baseRotateLevel : function()
26313 this.baseRotate = 1;
26316 typeof(this.exif) != 'undefined' &&
26317 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26318 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26320 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26323 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26327 baseScaleLevel : function()
26331 if(this.isDocument){
26333 if(this.baseRotate == 6 || this.baseRotate == 8){
26335 height = this.thumbEl.getHeight();
26336 this.baseScale = height / this.imageEl.OriginWidth;
26338 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26339 width = this.thumbEl.getWidth();
26340 this.baseScale = width / this.imageEl.OriginHeight;
26346 height = this.thumbEl.getHeight();
26347 this.baseScale = height / this.imageEl.OriginHeight;
26349 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26350 width = this.thumbEl.getWidth();
26351 this.baseScale = width / this.imageEl.OriginWidth;
26357 if(this.baseRotate == 6 || this.baseRotate == 8){
26359 width = this.thumbEl.getHeight();
26360 this.baseScale = width / this.imageEl.OriginHeight;
26362 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26363 height = this.thumbEl.getWidth();
26364 this.baseScale = height / this.imageEl.OriginHeight;
26367 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26368 height = this.thumbEl.getWidth();
26369 this.baseScale = height / this.imageEl.OriginHeight;
26371 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26372 width = this.thumbEl.getHeight();
26373 this.baseScale = width / this.imageEl.OriginWidth;
26380 width = this.thumbEl.getWidth();
26381 this.baseScale = width / this.imageEl.OriginWidth;
26383 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26384 height = this.thumbEl.getHeight();
26385 this.baseScale = height / this.imageEl.OriginHeight;
26388 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26390 height = this.thumbEl.getHeight();
26391 this.baseScale = height / this.imageEl.OriginHeight;
26393 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26394 width = this.thumbEl.getWidth();
26395 this.baseScale = width / this.imageEl.OriginWidth;
26403 getScaleLevel : function()
26405 return this.baseScale * Math.pow(1.1, this.scale);
26408 onTouchStart : function(e)
26410 if(!this.canvasLoaded){
26411 this.beforeSelectFile(e);
26415 var touches = e.browserEvent.touches;
26421 if(touches.length == 1){
26422 this.onMouseDown(e);
26426 if(touches.length != 2){
26432 for(var i = 0, finger; finger = touches[i]; i++){
26433 coords.push(finger.pageX, finger.pageY);
26436 var x = Math.pow(coords[0] - coords[2], 2);
26437 var y = Math.pow(coords[1] - coords[3], 2);
26439 this.startDistance = Math.sqrt(x + y);
26441 this.startScale = this.scale;
26443 this.pinching = true;
26444 this.dragable = false;
26448 onTouchMove : function(e)
26450 if(!this.pinching && !this.dragable){
26454 var touches = e.browserEvent.touches;
26461 this.onMouseMove(e);
26467 for(var i = 0, finger; finger = touches[i]; i++){
26468 coords.push(finger.pageX, finger.pageY);
26471 var x = Math.pow(coords[0] - coords[2], 2);
26472 var y = Math.pow(coords[1] - coords[3], 2);
26474 this.endDistance = Math.sqrt(x + y);
26476 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26478 if(!this.zoomable()){
26479 this.scale = this.startScale;
26487 onTouchEnd : function(e)
26489 this.pinching = false;
26490 this.dragable = false;
26494 process : function(file, crop)
26497 this.maskEl.mask(this.loadingText);
26500 this.xhr = new XMLHttpRequest();
26502 file.xhr = this.xhr;
26504 this.xhr.open(this.method, this.url, true);
26507 "Accept": "application/json",
26508 "Cache-Control": "no-cache",
26509 "X-Requested-With": "XMLHttpRequest"
26512 for (var headerName in headers) {
26513 var headerValue = headers[headerName];
26515 this.xhr.setRequestHeader(headerName, headerValue);
26521 this.xhr.onload = function()
26523 _this.xhrOnLoad(_this.xhr);
26526 this.xhr.onerror = function()
26528 _this.xhrOnError(_this.xhr);
26531 var formData = new FormData();
26533 formData.append('returnHTML', 'NO');
26536 formData.append('crop', crop);
26539 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26540 formData.append(this.paramName, file, file.name);
26543 if(typeof(file.filename) != 'undefined'){
26544 formData.append('filename', file.filename);
26547 if(typeof(file.mimetype) != 'undefined'){
26548 formData.append('mimetype', file.mimetype);
26551 if(this.fireEvent('arrange', this, formData) != false){
26552 this.xhr.send(formData);
26556 xhrOnLoad : function(xhr)
26559 this.maskEl.unmask();
26562 if (xhr.readyState !== 4) {
26563 this.fireEvent('exception', this, xhr);
26567 var response = Roo.decode(xhr.responseText);
26569 if(!response.success){
26570 this.fireEvent('exception', this, xhr);
26574 var response = Roo.decode(xhr.responseText);
26576 this.fireEvent('upload', this, response);
26580 xhrOnError : function()
26583 this.maskEl.unmask();
26586 Roo.log('xhr on error');
26588 var response = Roo.decode(xhr.responseText);
26594 prepare : function(file)
26597 this.maskEl.mask(this.loadingText);
26603 if(typeof(file) === 'string'){
26604 this.loadCanvas(file);
26608 if(!file || !this.urlAPI){
26613 this.cropType = file.type;
26617 if(this.fireEvent('prepare', this, this.file) != false){
26619 var reader = new FileReader();
26621 reader.onload = function (e) {
26622 if (e.target.error) {
26623 Roo.log(e.target.error);
26627 var buffer = e.target.result,
26628 dataView = new DataView(buffer),
26630 maxOffset = dataView.byteLength - 4,
26634 if (dataView.getUint16(0) === 0xffd8) {
26635 while (offset < maxOffset) {
26636 markerBytes = dataView.getUint16(offset);
26638 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26639 markerLength = dataView.getUint16(offset + 2) + 2;
26640 if (offset + markerLength > dataView.byteLength) {
26641 Roo.log('Invalid meta data: Invalid segment size.');
26645 if(markerBytes == 0xffe1){
26646 _this.parseExifData(
26653 offset += markerLength;
26663 var url = _this.urlAPI.createObjectURL(_this.file);
26665 _this.loadCanvas(url);
26670 reader.readAsArrayBuffer(this.file);
26676 parseExifData : function(dataView, offset, length)
26678 var tiffOffset = offset + 10,
26682 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26683 // No Exif data, might be XMP data instead
26687 // Check for the ASCII code for "Exif" (0x45786966):
26688 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26689 // No Exif data, might be XMP data instead
26692 if (tiffOffset + 8 > dataView.byteLength) {
26693 Roo.log('Invalid Exif data: Invalid segment size.');
26696 // Check for the two null bytes:
26697 if (dataView.getUint16(offset + 8) !== 0x0000) {
26698 Roo.log('Invalid Exif data: Missing byte alignment offset.');
26701 // Check the byte alignment:
26702 switch (dataView.getUint16(tiffOffset)) {
26704 littleEndian = true;
26707 littleEndian = false;
26710 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26713 // Check for the TIFF tag marker (0x002A):
26714 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26715 Roo.log('Invalid Exif data: Missing TIFF marker.');
26718 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26719 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26721 this.parseExifTags(
26724 tiffOffset + dirOffset,
26729 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26734 if (dirOffset + 6 > dataView.byteLength) {
26735 Roo.log('Invalid Exif data: Invalid directory offset.');
26738 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26739 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26740 if (dirEndOffset + 4 > dataView.byteLength) {
26741 Roo.log('Invalid Exif data: Invalid directory size.');
26744 for (i = 0; i < tagsNumber; i += 1) {
26748 dirOffset + 2 + 12 * i, // tag offset
26752 // Return the offset to the next directory:
26753 return dataView.getUint32(dirEndOffset, littleEndian);
26756 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
26758 var tag = dataView.getUint16(offset, littleEndian);
26760 this.exif[tag] = this.getExifValue(
26764 dataView.getUint16(offset + 2, littleEndian), // tag type
26765 dataView.getUint32(offset + 4, littleEndian), // tag length
26770 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26772 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26781 Roo.log('Invalid Exif data: Invalid tag type.');
26785 tagSize = tagType.size * length;
26786 // Determine if the value is contained in the dataOffset bytes,
26787 // or if the value at the dataOffset is a pointer to the actual data:
26788 dataOffset = tagSize > 4 ?
26789 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26790 if (dataOffset + tagSize > dataView.byteLength) {
26791 Roo.log('Invalid Exif data: Invalid data offset.');
26794 if (length === 1) {
26795 return tagType.getValue(dataView, dataOffset, littleEndian);
26798 for (i = 0; i < length; i += 1) {
26799 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26802 if (tagType.ascii) {
26804 // Concatenate the chars:
26805 for (i = 0; i < values.length; i += 1) {
26807 // Ignore the terminating NULL byte(s):
26808 if (c === '\u0000') {
26820 Roo.apply(Roo.bootstrap.UploadCropbox, {
26822 'Orientation': 0x0112
26826 1: 0, //'top-left',
26828 3: 180, //'bottom-right',
26829 // 4: 'bottom-left',
26831 6: 90, //'right-top',
26832 // 7: 'right-bottom',
26833 8: 270 //'left-bottom'
26837 // byte, 8-bit unsigned int:
26839 getValue: function (dataView, dataOffset) {
26840 return dataView.getUint8(dataOffset);
26844 // ascii, 8-bit byte:
26846 getValue: function (dataView, dataOffset) {
26847 return String.fromCharCode(dataView.getUint8(dataOffset));
26852 // short, 16 bit int:
26854 getValue: function (dataView, dataOffset, littleEndian) {
26855 return dataView.getUint16(dataOffset, littleEndian);
26859 // long, 32 bit int:
26861 getValue: function (dataView, dataOffset, littleEndian) {
26862 return dataView.getUint32(dataOffset, littleEndian);
26866 // rational = two long values, first is numerator, second is denominator:
26868 getValue: function (dataView, dataOffset, littleEndian) {
26869 return dataView.getUint32(dataOffset, littleEndian) /
26870 dataView.getUint32(dataOffset + 4, littleEndian);
26874 // slong, 32 bit signed int:
26876 getValue: function (dataView, dataOffset, littleEndian) {
26877 return dataView.getInt32(dataOffset, littleEndian);
26881 // srational, two slongs, first is numerator, second is denominator:
26883 getValue: function (dataView, dataOffset, littleEndian) {
26884 return dataView.getInt32(dataOffset, littleEndian) /
26885 dataView.getInt32(dataOffset + 4, littleEndian);
26895 cls : 'btn-group roo-upload-cropbox-rotate-left',
26896 action : 'rotate-left',
26900 cls : 'btn btn-default',
26901 html : '<i class="fa fa-undo"></i>'
26907 cls : 'btn-group roo-upload-cropbox-picture',
26908 action : 'picture',
26912 cls : 'btn btn-default',
26913 html : '<i class="fa fa-picture-o"></i>'
26919 cls : 'btn-group roo-upload-cropbox-rotate-right',
26920 action : 'rotate-right',
26924 cls : 'btn btn-default',
26925 html : '<i class="fa fa-repeat"></i>'
26933 cls : 'btn-group roo-upload-cropbox-rotate-left',
26934 action : 'rotate-left',
26938 cls : 'btn btn-default',
26939 html : '<i class="fa fa-undo"></i>'
26945 cls : 'btn-group roo-upload-cropbox-download',
26946 action : 'download',
26950 cls : 'btn btn-default',
26951 html : '<i class="fa fa-download"></i>'
26957 cls : 'btn-group roo-upload-cropbox-crop',
26962 cls : 'btn btn-default',
26963 html : '<i class="fa fa-crop"></i>'
26969 cls : 'btn-group roo-upload-cropbox-trash',
26974 cls : 'btn btn-default',
26975 html : '<i class="fa fa-trash"></i>'
26981 cls : 'btn-group roo-upload-cropbox-rotate-right',
26982 action : 'rotate-right',
26986 cls : 'btn btn-default',
26987 html : '<i class="fa fa-repeat"></i>'
26995 cls : 'btn-group roo-upload-cropbox-rotate-left',
26996 action : 'rotate-left',
27000 cls : 'btn btn-default',
27001 html : '<i class="fa fa-undo"></i>'
27007 cls : 'btn-group roo-upload-cropbox-rotate-right',
27008 action : 'rotate-right',
27012 cls : 'btn btn-default',
27013 html : '<i class="fa fa-repeat"></i>'
27026 * @class Roo.bootstrap.DocumentManager
27027 * @extends Roo.bootstrap.Component
27028 * Bootstrap DocumentManager class
27029 * @cfg {String} paramName default 'imageUpload'
27030 * @cfg {String} method default POST
27031 * @cfg {String} url action url
27032 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27033 * @cfg {Boolean} multiple multiple upload default true
27034 * @cfg {Number} thumbSize default 300
27035 * @cfg {String} fieldLabel
27036 * @cfg {Number} labelWidth default 4
27037 * @cfg {String} labelAlign (left|top) default left
27038 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27041 * Create a new DocumentManager
27042 * @param {Object} config The config object
27045 Roo.bootstrap.DocumentManager = function(config){
27046 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27051 * Fire when initial the DocumentManager
27052 * @param {Roo.bootstrap.DocumentManager} this
27057 * inspect selected file
27058 * @param {Roo.bootstrap.DocumentManager} this
27059 * @param {File} file
27064 * Fire when xhr load exception
27065 * @param {Roo.bootstrap.DocumentManager} this
27066 * @param {XMLHttpRequest} xhr
27068 "exception" : true,
27071 * prepare the form data
27072 * @param {Roo.bootstrap.DocumentManager} this
27073 * @param {Object} formData
27078 * Fire when remove the file
27079 * @param {Roo.bootstrap.DocumentManager} this
27080 * @param {Object} file
27085 * Fire after refresh the file
27086 * @param {Roo.bootstrap.DocumentManager} this
27091 * Fire after click the image
27092 * @param {Roo.bootstrap.DocumentManager} this
27093 * @param {Object} file
27098 * Fire when upload a image and editable set to true
27099 * @param {Roo.bootstrap.DocumentManager} this
27100 * @param {Object} file
27104 * @event beforeselectfile
27105 * Fire before select file
27106 * @param {Roo.bootstrap.DocumentManager} this
27108 "beforeselectfile" : true,
27111 * Fire before process file
27112 * @param {Roo.bootstrap.DocumentManager} this
27113 * @param {Object} file
27120 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
27129 paramName : 'imageUpload',
27132 labelAlign : 'left',
27139 getAutoCreate : function()
27141 var managerWidget = {
27143 cls : 'roo-document-manager',
27147 cls : 'roo-document-manager-selector',
27152 cls : 'roo-document-manager-uploader',
27156 cls : 'roo-document-manager-upload-btn',
27157 html : '<i class="fa fa-plus"></i>'
27168 cls : 'column col-md-12',
27173 if(this.fieldLabel.length){
27178 cls : 'column col-md-12',
27179 html : this.fieldLabel
27183 cls : 'column col-md-12',
27188 if(this.labelAlign == 'left'){
27192 cls : 'column col-md-' + this.labelWidth,
27193 html : this.fieldLabel
27197 cls : 'column col-md-' + (12 - this.labelWidth),
27207 cls : 'row clearfix',
27215 initEvents : function()
27217 this.managerEl = this.el.select('.roo-document-manager', true).first();
27218 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27220 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27221 this.selectorEl.hide();
27224 this.selectorEl.attr('multiple', 'multiple');
27227 this.selectorEl.on('change', this.onFileSelected, this);
27229 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27230 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27232 this.uploader.on('click', this.onUploaderClick, this);
27234 this.renderProgressDialog();
27238 window.addEventListener("resize", function() { _this.refresh(); } );
27240 this.fireEvent('initial', this);
27243 renderProgressDialog : function()
27247 this.progressDialog = new Roo.bootstrap.Modal({
27248 cls : 'roo-document-manager-progress-dialog',
27249 allow_close : false,
27259 btnclick : function() {
27260 _this.uploadCancel();
27266 this.progressDialog.render(Roo.get(document.body));
27268 this.progress = new Roo.bootstrap.Progress({
27269 cls : 'roo-document-manager-progress',
27274 this.progress.render(this.progressDialog.getChildContainer());
27276 this.progressBar = new Roo.bootstrap.ProgressBar({
27277 cls : 'roo-document-manager-progress-bar',
27280 aria_valuemax : 12,
27284 this.progressBar.render(this.progress.getChildContainer());
27287 onUploaderClick : function(e)
27289 e.preventDefault();
27291 if(this.fireEvent('beforeselectfile', this) != false){
27292 this.selectorEl.dom.click();
27297 onFileSelected : function(e)
27299 e.preventDefault();
27301 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27305 Roo.each(this.selectorEl.dom.files, function(file){
27306 if(this.fireEvent('inspect', this, file) != false){
27307 this.files.push(file);
27317 this.selectorEl.dom.value = '';
27319 if(!this.files.length){
27323 if(this.boxes > 0 && this.files.length > this.boxes){
27324 this.files = this.files.slice(0, this.boxes);
27327 this.uploader.show();
27329 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27330 this.uploader.hide();
27339 Roo.each(this.files, function(file){
27341 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27342 var f = this.renderPreview(file);
27347 if(file.type.indexOf('image') != -1){
27348 this.delegates.push(
27350 _this.process(file);
27351 }).createDelegate(this)
27359 _this.process(file);
27360 }).createDelegate(this)
27365 this.files = files;
27367 this.delegates = this.delegates.concat(docs);
27369 if(!this.delegates.length){
27374 this.progressBar.aria_valuemax = this.delegates.length;
27381 arrange : function()
27383 if(!this.delegates.length){
27384 this.progressDialog.hide();
27389 var delegate = this.delegates.shift();
27391 this.progressDialog.show();
27393 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27395 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27400 refresh : function()
27402 this.uploader.show();
27404 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27405 this.uploader.hide();
27408 Roo.isTouch ? this.closable(false) : this.closable(true);
27410 this.fireEvent('refresh', this);
27413 onRemove : function(e, el, o)
27415 e.preventDefault();
27417 this.fireEvent('remove', this, o);
27421 remove : function(o)
27425 Roo.each(this.files, function(file){
27426 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27435 this.files = files;
27442 Roo.each(this.files, function(file){
27447 file.target.remove();
27456 onClick : function(e, el, o)
27458 e.preventDefault();
27460 this.fireEvent('click', this, o);
27464 closable : function(closable)
27466 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27468 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27480 xhrOnLoad : function(xhr)
27482 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27486 if (xhr.readyState !== 4) {
27488 this.fireEvent('exception', this, xhr);
27492 var response = Roo.decode(xhr.responseText);
27494 if(!response.success){
27496 this.fireEvent('exception', this, xhr);
27500 var file = this.renderPreview(response.data);
27502 this.files.push(file);
27508 xhrOnError : function(xhr)
27510 Roo.log('xhr on error');
27512 var response = Roo.decode(xhr.responseText);
27519 process : function(file)
27521 if(this.fireEvent('process', this, file) !== false){
27522 if(this.editable && file.type.indexOf('image') != -1){
27523 this.fireEvent('edit', this, file);
27527 this.uploadStart(file, false);
27534 uploadStart : function(file, crop)
27536 this.xhr = new XMLHttpRequest();
27538 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27543 file.xhr = this.xhr;
27545 this.managerEl.createChild({
27547 cls : 'roo-document-manager-loading',
27551 tooltip : file.name,
27552 cls : 'roo-document-manager-thumb',
27553 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27559 this.xhr.open(this.method, this.url, true);
27562 "Accept": "application/json",
27563 "Cache-Control": "no-cache",
27564 "X-Requested-With": "XMLHttpRequest"
27567 for (var headerName in headers) {
27568 var headerValue = headers[headerName];
27570 this.xhr.setRequestHeader(headerName, headerValue);
27576 this.xhr.onload = function()
27578 _this.xhrOnLoad(_this.xhr);
27581 this.xhr.onerror = function()
27583 _this.xhrOnError(_this.xhr);
27586 var formData = new FormData();
27588 formData.append('returnHTML', 'NO');
27591 formData.append('crop', crop);
27594 formData.append(this.paramName, file, file.name);
27596 if(this.fireEvent('prepare', this, formData) != false){
27597 this.xhr.send(formData);
27601 uploadCancel : function()
27608 this.delegates = [];
27610 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27617 renderPreview : function(file)
27619 if(typeof(file.target) != 'undefined' && file.target){
27623 var previewEl = this.managerEl.createChild({
27625 cls : 'roo-document-manager-preview',
27629 tooltip : file.filename,
27630 cls : 'roo-document-manager-thumb',
27631 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27636 html : '<i class="fa fa-times-circle"></i>'
27641 var close = previewEl.select('button.close', true).first();
27643 close.on('click', this.onRemove, this, file);
27645 file.target = previewEl;
27647 var image = previewEl.select('img', true).first();
27651 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27653 image.on('click', this.onClick, this, file);
27659 onPreviewLoad : function(file, image)
27661 if(typeof(file.target) == 'undefined' || !file.target){
27665 var width = image.dom.naturalWidth || image.dom.width;
27666 var height = image.dom.naturalHeight || image.dom.height;
27668 if(width > height){
27669 file.target.addClass('wide');
27673 file.target.addClass('tall');
27678 uploadFromSource : function(file, crop)
27680 this.xhr = new XMLHttpRequest();
27682 this.managerEl.createChild({
27684 cls : 'roo-document-manager-loading',
27688 tooltip : file.name,
27689 cls : 'roo-document-manager-thumb',
27690 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27696 this.xhr.open(this.method, this.url, true);
27699 "Accept": "application/json",
27700 "Cache-Control": "no-cache",
27701 "X-Requested-With": "XMLHttpRequest"
27704 for (var headerName in headers) {
27705 var headerValue = headers[headerName];
27707 this.xhr.setRequestHeader(headerName, headerValue);
27713 this.xhr.onload = function()
27715 _this.xhrOnLoad(_this.xhr);
27718 this.xhr.onerror = function()
27720 _this.xhrOnError(_this.xhr);
27723 var formData = new FormData();
27725 formData.append('returnHTML', 'NO');
27727 formData.append('crop', crop);
27729 if(typeof(file.filename) != 'undefined'){
27730 formData.append('filename', file.filename);
27733 if(typeof(file.mimetype) != 'undefined'){
27734 formData.append('mimetype', file.mimetype);
27737 if(this.fireEvent('prepare', this, formData) != false){
27738 this.xhr.send(formData);
27748 * @class Roo.bootstrap.DocumentViewer
27749 * @extends Roo.bootstrap.Component
27750 * Bootstrap DocumentViewer class
27753 * Create a new DocumentViewer
27754 * @param {Object} config The config object
27757 Roo.bootstrap.DocumentViewer = function(config){
27758 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27763 * Fire after initEvent
27764 * @param {Roo.bootstrap.DocumentViewer} this
27770 * @param {Roo.bootstrap.DocumentViewer} this
27775 * Fire after trash button
27776 * @param {Roo.bootstrap.DocumentViewer} this
27783 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27785 getAutoCreate : function()
27789 cls : 'roo-document-viewer',
27793 cls : 'roo-document-viewer-body',
27797 cls : 'roo-document-viewer-thumb',
27801 cls : 'roo-document-viewer-image'
27809 cls : 'roo-document-viewer-footer',
27812 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27820 cls : 'btn btn-default roo-document-viewer-trash',
27821 html : '<i class="fa fa-trash"></i>'
27834 initEvents : function()
27837 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27838 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27840 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27841 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27843 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27844 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27846 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27847 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27849 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27850 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27852 this.bodyEl.on('click', this.onClick, this);
27854 this.trashBtn.on('click', this.onTrash, this);
27858 initial : function()
27860 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27863 this.fireEvent('initial', this);
27867 onClick : function(e)
27869 e.preventDefault();
27871 this.fireEvent('click', this);
27874 onTrash : function(e)
27876 e.preventDefault();
27878 this.fireEvent('trash', this);
27890 * @class Roo.bootstrap.NavProgressBar
27891 * @extends Roo.bootstrap.Component
27892 * Bootstrap NavProgressBar class
27895 * Create a new nav progress bar
27896 * @param {Object} config The config object
27899 Roo.bootstrap.NavProgressBar = function(config){
27900 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27902 this.bullets = this.bullets || [];
27904 // Roo.bootstrap.NavProgressBar.register(this);
27908 * Fires when the active item changes
27909 * @param {Roo.bootstrap.NavProgressBar} this
27910 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27911 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27918 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27923 getAutoCreate : function()
27925 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27929 cls : 'roo-navigation-bar-group',
27933 cls : 'roo-navigation-top-bar'
27937 cls : 'roo-navigation-bullets-bar',
27941 cls : 'roo-navigation-bar'
27948 cls : 'roo-navigation-bottom-bar'
27958 initEvents: function()
27963 onRender : function(ct, position)
27965 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27967 if(this.bullets.length){
27968 Roo.each(this.bullets, function(b){
27977 addItem : function(cfg)
27979 var item = new Roo.bootstrap.NavProgressItem(cfg);
27981 item.parentId = this.id;
27982 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27985 var top = new Roo.bootstrap.Element({
27987 cls : 'roo-navigation-bar-text'
27990 var bottom = new Roo.bootstrap.Element({
27992 cls : 'roo-navigation-bar-text'
27995 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27996 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27998 var topText = new Roo.bootstrap.Element({
28000 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28003 var bottomText = new Roo.bootstrap.Element({
28005 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28008 topText.onRender(top.el, null);
28009 bottomText.onRender(bottom.el, null);
28012 item.bottomEl = bottom;
28015 this.barItems.push(item);
28020 getActive : function()
28022 var active = false;
28024 Roo.each(this.barItems, function(v){
28026 if (!v.isActive()) {
28038 setActiveItem : function(item)
28042 Roo.each(this.barItems, function(v){
28043 if (v.rid == item.rid) {
28047 if (v.isActive()) {
28048 v.setActive(false);
28053 item.setActive(true);
28055 this.fireEvent('changed', this, item, prev);
28058 getBarItem: function(rid)
28062 Roo.each(this.barItems, function(e) {
28063 if (e.rid != rid) {
28074 indexOfItem : function(item)
28078 Roo.each(this.barItems, function(v, i){
28080 if (v.rid != item.rid) {
28091 setActiveNext : function()
28093 var i = this.indexOfItem(this.getActive());
28095 if (i > this.barItems.length) {
28099 this.setActiveItem(this.barItems[i+1]);
28102 setActivePrev : function()
28104 var i = this.indexOfItem(this.getActive());
28110 this.setActiveItem(this.barItems[i-1]);
28113 format : function()
28115 if(!this.barItems.length){
28119 var width = 100 / this.barItems.length;
28121 Roo.each(this.barItems, function(i){
28122 i.el.setStyle('width', width + '%');
28123 i.topEl.el.setStyle('width', width + '%');
28124 i.bottomEl.el.setStyle('width', width + '%');
28133 * Nav Progress Item
28138 * @class Roo.bootstrap.NavProgressItem
28139 * @extends Roo.bootstrap.Component
28140 * Bootstrap NavProgressItem class
28141 * @cfg {String} rid the reference id
28142 * @cfg {Boolean} active (true|false) Is item active default false
28143 * @cfg {Boolean} disabled (true|false) Is item active default false
28144 * @cfg {String} html
28145 * @cfg {String} position (top|bottom) text position default bottom
28146 * @cfg {String} icon show icon instead of number
28149 * Create a new NavProgressItem
28150 * @param {Object} config The config object
28152 Roo.bootstrap.NavProgressItem = function(config){
28153 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28158 * The raw click event for the entire grid.
28159 * @param {Roo.bootstrap.NavProgressItem} this
28160 * @param {Roo.EventObject} e
28167 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
28173 position : 'bottom',
28176 getAutoCreate : function()
28178 var iconCls = 'roo-navigation-bar-item-icon';
28180 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28184 cls: 'roo-navigation-bar-item',
28194 cfg.cls += ' active';
28197 cfg.cls += ' disabled';
28203 disable : function()
28205 this.setDisabled(true);
28208 enable : function()
28210 this.setDisabled(false);
28213 initEvents: function()
28215 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28217 this.iconEl.on('click', this.onClick, this);
28220 onClick : function(e)
28222 e.preventDefault();
28228 if(this.fireEvent('click', this, e) === false){
28232 this.parent().setActiveItem(this);
28235 isActive: function ()
28237 return this.active;
28240 setActive : function(state)
28242 if(this.active == state){
28246 this.active = state;
28249 this.el.addClass('active');
28253 this.el.removeClass('active');
28258 setDisabled : function(state)
28260 if(this.disabled == state){
28264 this.disabled = state;
28267 this.el.addClass('disabled');
28271 this.el.removeClass('disabled');
28274 tooltipEl : function()
28276 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28289 * @class Roo.bootstrap.FieldLabel
28290 * @extends Roo.bootstrap.Component
28291 * Bootstrap FieldLabel class
28292 * @cfg {String} html contents of the element
28293 * @cfg {String} tag tag of the element default label
28294 * @cfg {String} cls class of the element
28295 * @cfg {String} target label target
28296 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28297 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28298 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28299 * @cfg {String} iconTooltip default "This field is required"
28302 * Create a new FieldLabel
28303 * @param {Object} config The config object
28306 Roo.bootstrap.FieldLabel = function(config){
28307 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28312 * Fires after the field has been marked as invalid.
28313 * @param {Roo.form.FieldLabel} this
28314 * @param {String} msg The validation message
28319 * Fires after the field has been validated with no errors.
28320 * @param {Roo.form.FieldLabel} this
28326 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28333 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28334 validClass : 'text-success fa fa-lg fa-check',
28335 iconTooltip : 'This field is required',
28337 getAutoCreate : function(){
28341 cls : 'roo-bootstrap-field-label ' + this.cls,
28347 tooltip : this.iconTooltip
28359 initEvents: function()
28361 Roo.bootstrap.Element.superclass.initEvents.call(this);
28363 this.iconEl = this.el.select('i', true).first();
28365 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28367 Roo.bootstrap.FieldLabel.register(this);
28371 * Mark this field as valid
28373 markValid : function()
28375 this.iconEl.show();
28377 this.iconEl.removeClass(this.invalidClass);
28379 this.iconEl.addClass(this.validClass);
28381 this.fireEvent('valid', this);
28385 * Mark this field as invalid
28386 * @param {String} msg The validation message
28388 markInvalid : function(msg)
28390 this.iconEl.show();
28392 this.iconEl.removeClass(this.validClass);
28394 this.iconEl.addClass(this.invalidClass);
28396 this.fireEvent('invalid', this, msg);
28402 Roo.apply(Roo.bootstrap.FieldLabel, {
28407 * register a FieldLabel Group
28408 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28410 register : function(label)
28412 if(this.groups.hasOwnProperty(label.target)){
28416 this.groups[label.target] = label;
28420 * fetch a FieldLabel Group based on the target
28421 * @param {string} target
28422 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28424 get: function(target) {
28425 if (typeof(this.groups[target]) == 'undefined') {
28429 return this.groups[target] ;
28438 * page DateSplitField.
28444 * @class Roo.bootstrap.DateSplitField
28445 * @extends Roo.bootstrap.Component
28446 * Bootstrap DateSplitField class
28447 * @cfg {string} fieldLabel - the label associated
28448 * @cfg {Number} labelWidth set the width of label (0-12)
28449 * @cfg {String} labelAlign (top|left)
28450 * @cfg {Boolean} dayAllowBlank (true|false) default false
28451 * @cfg {Boolean} monthAllowBlank (true|false) default false
28452 * @cfg {Boolean} yearAllowBlank (true|false) default false
28453 * @cfg {string} dayPlaceholder
28454 * @cfg {string} monthPlaceholder
28455 * @cfg {string} yearPlaceholder
28456 * @cfg {string} dayFormat default 'd'
28457 * @cfg {string} monthFormat default 'm'
28458 * @cfg {string} yearFormat default 'Y'
28462 * Create a new DateSplitField
28463 * @param {Object} config The config object
28466 Roo.bootstrap.DateSplitField = function(config){
28467 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28473 * getting the data of years
28474 * @param {Roo.bootstrap.DateSplitField} this
28475 * @param {Object} years
28480 * getting the data of days
28481 * @param {Roo.bootstrap.DateSplitField} this
28482 * @param {Object} days
28487 * Fires after the field has been marked as invalid.
28488 * @param {Roo.form.Field} this
28489 * @param {String} msg The validation message
28494 * Fires after the field has been validated with no errors.
28495 * @param {Roo.form.Field} this
28501 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28504 labelAlign : 'top',
28506 dayAllowBlank : false,
28507 monthAllowBlank : false,
28508 yearAllowBlank : false,
28509 dayPlaceholder : '',
28510 monthPlaceholder : '',
28511 yearPlaceholder : '',
28515 isFormField : true,
28517 getAutoCreate : function()
28521 cls : 'row roo-date-split-field-group',
28526 cls : 'form-hidden-field roo-date-split-field-group-value',
28532 if(this.fieldLabel){
28535 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28539 html : this.fieldLabel
28545 Roo.each(['day', 'month', 'year'], function(t){
28548 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28555 inputEl: function ()
28557 return this.el.select('.roo-date-split-field-group-value', true).first();
28560 onRender : function(ct, position)
28564 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28566 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28568 this.dayField = new Roo.bootstrap.ComboBox({
28569 allowBlank : this.dayAllowBlank,
28570 alwaysQuery : true,
28571 displayField : 'value',
28574 forceSelection : true,
28576 placeholder : this.dayPlaceholder,
28577 selectOnFocus : true,
28578 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28579 triggerAction : 'all',
28581 valueField : 'value',
28582 store : new Roo.data.SimpleStore({
28583 data : (function() {
28585 _this.fireEvent('days', _this, days);
28588 fields : [ 'value' ]
28591 select : function (_self, record, index)
28593 _this.setValue(_this.getValue());
28598 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28600 this.monthField = new Roo.bootstrap.MonthField({
28601 after : '<i class=\"fa fa-calendar\"></i>',
28602 allowBlank : this.monthAllowBlank,
28603 placeholder : this.monthPlaceholder,
28606 render : function (_self)
28608 this.el.select('span.input-group-addon', true).first().on('click', function(e){
28609 e.preventDefault();
28613 select : function (_self, oldvalue, newvalue)
28615 _this.setValue(_this.getValue());
28620 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28622 this.yearField = new Roo.bootstrap.ComboBox({
28623 allowBlank : this.yearAllowBlank,
28624 alwaysQuery : true,
28625 displayField : 'value',
28628 forceSelection : true,
28630 placeholder : this.yearPlaceholder,
28631 selectOnFocus : true,
28632 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28633 triggerAction : 'all',
28635 valueField : 'value',
28636 store : new Roo.data.SimpleStore({
28637 data : (function() {
28639 _this.fireEvent('years', _this, years);
28642 fields : [ 'value' ]
28645 select : function (_self, record, index)
28647 _this.setValue(_this.getValue());
28652 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28655 setValue : function(v, format)
28657 this.inputEl.dom.value = v;
28659 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28661 var d = Date.parseDate(v, f);
28668 this.setDay(d.format(this.dayFormat));
28669 this.setMonth(d.format(this.monthFormat));
28670 this.setYear(d.format(this.yearFormat));
28677 setDay : function(v)
28679 this.dayField.setValue(v);
28680 this.inputEl.dom.value = this.getValue();
28685 setMonth : function(v)
28687 this.monthField.setValue(v, true);
28688 this.inputEl.dom.value = this.getValue();
28693 setYear : function(v)
28695 this.yearField.setValue(v);
28696 this.inputEl.dom.value = this.getValue();
28701 getDay : function()
28703 return this.dayField.getValue();
28706 getMonth : function()
28708 return this.monthField.getValue();
28711 getYear : function()
28713 return this.yearField.getValue();
28716 getValue : function()
28718 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28720 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28730 this.inputEl.dom.value = '';
28735 validate : function()
28737 var d = this.dayField.validate();
28738 var m = this.monthField.validate();
28739 var y = this.yearField.validate();
28744 (!this.dayAllowBlank && !d) ||
28745 (!this.monthAllowBlank && !m) ||
28746 (!this.yearAllowBlank && !y)
28751 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28760 this.markInvalid();
28765 markValid : function()
28768 var label = this.el.select('label', true).first();
28769 var icon = this.el.select('i.fa-star', true).first();
28775 this.fireEvent('valid', this);
28779 * Mark this field as invalid
28780 * @param {String} msg The validation message
28782 markInvalid : function(msg)
28785 var label = this.el.select('label', true).first();
28786 var icon = this.el.select('i.fa-star', true).first();
28788 if(label && !icon){
28789 this.el.select('.roo-date-split-field-label', true).createChild({
28791 cls : 'text-danger fa fa-lg fa-star',
28792 tooltip : 'This field is required',
28793 style : 'margin-right:5px;'
28797 this.fireEvent('invalid', this, msg);
28800 clearInvalid : function()
28802 var label = this.el.select('label', true).first();
28803 var icon = this.el.select('i.fa-star', true).first();
28809 this.fireEvent('valid', this);
28812 getName: function()
28822 * http://masonry.desandro.com
28824 * The idea is to render all the bricks based on vertical width...
28826 * The original code extends 'outlayer' - we might need to use that....
28832 * @class Roo.bootstrap.LayoutMasonry
28833 * @extends Roo.bootstrap.Component
28834 * Bootstrap Layout Masonry class
28837 * Create a new Element
28838 * @param {Object} config The config object
28841 Roo.bootstrap.LayoutMasonry = function(config){
28842 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28848 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
28851 * @cfg {Boolean} isLayoutInstant = no animation?
28853 isLayoutInstant : false, // needed?
28856 * @cfg {Number} boxWidth width of the columns
28861 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
28866 * @cfg {Number} padWidth padding below box..
28871 * @cfg {Number} gutter gutter width..
28876 * @cfg {Number} maxCols maximum number of columns
28882 * @cfg {Boolean} isAutoInitial defalut true
28884 isAutoInitial : true,
28889 * @cfg {Boolean} isHorizontal defalut false
28891 isHorizontal : false,
28893 currentSize : null,
28899 bricks: null, //CompositeElement
28903 _isLayoutInited : false,
28905 // isAlternative : false, // only use for vertical layout...
28908 * @cfg {Number} alternativePadWidth padding below box..
28910 alternativePadWidth : 50,
28912 getAutoCreate : function(){
28916 cls: 'blog-masonary-wrapper ' + this.cls,
28918 cls : 'mas-boxes masonary'
28925 getChildContainer: function( )
28927 if (this.boxesEl) {
28928 return this.boxesEl;
28931 this.boxesEl = this.el.select('.mas-boxes').first();
28933 return this.boxesEl;
28937 initEvents : function()
28941 if(this.isAutoInitial){
28942 Roo.log('hook children rendered');
28943 this.on('childrenrendered', function() {
28944 Roo.log('children rendered');
28950 initial : function()
28952 this.currentSize = this.el.getBox(true);
28954 Roo.EventManager.onWindowResize(this.resize, this);
28956 if(!this.isAutoInitial){
28964 //this.layout.defer(500,this);
28968 resize : function()
28972 var cs = this.el.getBox(true);
28974 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28975 Roo.log("no change in with or X");
28979 this.currentSize = cs;
28985 layout : function()
28987 this._resetLayout();
28989 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28991 this.layoutItems( isInstant );
28993 this._isLayoutInited = true;
28997 _resetLayout : function()
28999 if(this.isHorizontal){
29000 this.horizontalMeasureColumns();
29004 this.verticalMeasureColumns();
29008 verticalMeasureColumns : function()
29010 this.getContainerWidth();
29012 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29013 // this.colWidth = Math.floor(this.containerWidth * 0.8);
29017 var boxWidth = this.boxWidth + this.padWidth;
29019 if(this.containerWidth < this.boxWidth){
29020 boxWidth = this.containerWidth
29023 var containerWidth = this.containerWidth;
29025 var cols = Math.floor(containerWidth / boxWidth);
29027 this.cols = Math.max( cols, 1 );
29029 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29031 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29033 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29035 this.colWidth = boxWidth + avail - this.padWidth;
29037 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29038 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
29041 horizontalMeasureColumns : function()
29043 this.getContainerWidth();
29045 var boxWidth = this.boxWidth;
29047 if(this.containerWidth < boxWidth){
29048 boxWidth = this.containerWidth;
29051 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29053 this.el.setHeight(boxWidth);
29057 getContainerWidth : function()
29059 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
29062 layoutItems : function( isInstant )
29064 var items = Roo.apply([], this.bricks);
29066 if(this.isHorizontal){
29067 this._horizontalLayoutItems( items , isInstant );
29071 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29072 // this._verticalAlternativeLayoutItems( items , isInstant );
29076 this._verticalLayoutItems( items , isInstant );
29080 _verticalLayoutItems : function ( items , isInstant)
29082 if ( !items || !items.length ) {
29087 ['xs', 'xs', 'xs', 'tall'],
29088 ['xs', 'xs', 'tall'],
29089 ['xs', 'xs', 'sm'],
29090 ['xs', 'xs', 'xs'],
29096 ['sm', 'xs', 'xs'],
29100 ['tall', 'xs', 'xs', 'xs'],
29101 ['tall', 'xs', 'xs'],
29113 Roo.each(items, function(item, k){
29115 switch (item.size) {
29116 // these layouts take up a full box,
29127 boxes.push([item]);
29150 var filterPattern = function(box, length)
29158 var pattern = box.slice(0, length);
29162 Roo.each(pattern, function(i){
29163 format.push(i.size);
29166 Roo.each(standard, function(s){
29168 if(String(s) != String(format)){
29177 if(!match && length == 1){
29182 filterPattern(box, length - 1);
29186 queue.push(pattern);
29188 box = box.slice(length, box.length);
29190 filterPattern(box, 4);
29196 Roo.each(boxes, function(box, k){
29202 if(box.length == 1){
29207 filterPattern(box, 4);
29211 this._processVerticalLayoutQueue( queue, isInstant );
29215 // _verticalAlternativeLayoutItems : function( items , isInstant )
29217 // if ( !items || !items.length ) {
29221 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
29225 _horizontalLayoutItems : function ( items , isInstant)
29227 if ( !items || !items.length || items.length < 3) {
29233 var eItems = items.slice(0, 3);
29235 items = items.slice(3, items.length);
29238 ['xs', 'xs', 'xs', 'wide'],
29239 ['xs', 'xs', 'wide'],
29240 ['xs', 'xs', 'sm'],
29241 ['xs', 'xs', 'xs'],
29247 ['sm', 'xs', 'xs'],
29251 ['wide', 'xs', 'xs', 'xs'],
29252 ['wide', 'xs', 'xs'],
29265 Roo.each(items, function(item, k){
29267 switch (item.size) {
29278 boxes.push([item]);
29302 var filterPattern = function(box, length)
29310 var pattern = box.slice(0, length);
29314 Roo.each(pattern, function(i){
29315 format.push(i.size);
29318 Roo.each(standard, function(s){
29320 if(String(s) != String(format)){
29329 if(!match && length == 1){
29334 filterPattern(box, length - 1);
29338 queue.push(pattern);
29340 box = box.slice(length, box.length);
29342 filterPattern(box, 4);
29348 Roo.each(boxes, function(box, k){
29354 if(box.length == 1){
29359 filterPattern(box, 4);
29366 var pos = this.el.getBox(true);
29370 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29372 var hit_end = false;
29374 Roo.each(queue, function(box){
29378 Roo.each(box, function(b){
29380 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29390 Roo.each(box, function(b){
29392 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29395 mx = Math.max(mx, b.x);
29399 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29403 Roo.each(box, function(b){
29405 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29419 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29422 /** Sets position of item in DOM
29423 * @param {Element} item
29424 * @param {Number} x - horizontal position
29425 * @param {Number} y - vertical position
29426 * @param {Boolean} isInstant - disables transitions
29428 _processVerticalLayoutQueue : function( queue, isInstant )
29430 var pos = this.el.getBox(true);
29435 for (var i = 0; i < this.cols; i++){
29439 Roo.each(queue, function(box, k){
29441 var col = k % this.cols;
29443 Roo.each(box, function(b,kk){
29445 b.el.position('absolute');
29447 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29448 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29450 if(b.size == 'md-left' || b.size == 'md-right'){
29451 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29452 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29455 b.el.setWidth(width);
29456 b.el.setHeight(height);
29458 b.el.select('iframe',true).setSize(width,height);
29462 for (var i = 0; i < this.cols; i++){
29464 if(maxY[i] < maxY[col]){
29469 col = Math.min(col, i);
29473 x = pos.x + col * (this.colWidth + this.padWidth);
29477 var positions = [];
29479 switch (box.length){
29481 positions = this.getVerticalOneBoxColPositions(x, y, box);
29484 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29487 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29490 positions = this.getVerticalFourBoxColPositions(x, y, box);
29496 Roo.each(box, function(b,kk){
29498 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29500 var sz = b.el.getSize();
29502 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29510 for (var i = 0; i < this.cols; i++){
29511 mY = Math.max(mY, maxY[i]);
29514 this.el.setHeight(mY - pos.y);
29518 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29520 // var pos = this.el.getBox(true);
29523 // var maxX = pos.right;
29525 // var maxHeight = 0;
29527 // Roo.each(items, function(item, k){
29531 // item.el.position('absolute');
29533 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29535 // item.el.setWidth(width);
29537 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29539 // item.el.setHeight(height);
29542 // item.el.setXY([x, y], isInstant ? false : true);
29544 // item.el.setXY([maxX - width, y], isInstant ? false : true);
29547 // y = y + height + this.alternativePadWidth;
29549 // maxHeight = maxHeight + height + this.alternativePadWidth;
29553 // this.el.setHeight(maxHeight);
29557 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29559 var pos = this.el.getBox(true);
29564 var maxX = pos.right;
29566 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29568 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29570 Roo.each(queue, function(box, k){
29572 Roo.each(box, function(b, kk){
29574 b.el.position('absolute');
29576 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29577 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29579 if(b.size == 'md-left' || b.size == 'md-right'){
29580 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29581 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29584 b.el.setWidth(width);
29585 b.el.setHeight(height);
29593 var positions = [];
29595 switch (box.length){
29597 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29600 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29603 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29606 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29612 Roo.each(box, function(b,kk){
29614 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29616 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29624 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29626 Roo.each(eItems, function(b,k){
29628 b.size = (k == 0) ? 'sm' : 'xs';
29629 b.x = (k == 0) ? 2 : 1;
29630 b.y = (k == 0) ? 2 : 1;
29632 b.el.position('absolute');
29634 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29636 b.el.setWidth(width);
29638 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29640 b.el.setHeight(height);
29644 var positions = [];
29647 x : maxX - this.unitWidth * 2 - this.gutter,
29652 x : maxX - this.unitWidth,
29653 y : minY + (this.unitWidth + this.gutter) * 2
29657 x : maxX - this.unitWidth * 3 - this.gutter * 2,
29661 Roo.each(eItems, function(b,k){
29663 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29669 getVerticalOneBoxColPositions : function(x, y, box)
29673 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29675 if(box[0].size == 'md-left'){
29679 if(box[0].size == 'md-right'){
29684 x : x + (this.unitWidth + this.gutter) * rand,
29691 getVerticalTwoBoxColPositions : function(x, y, box)
29695 if(box[0].size == 'xs'){
29699 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29703 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29717 x : x + (this.unitWidth + this.gutter) * 2,
29718 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29725 getVerticalThreeBoxColPositions : function(x, y, box)
29729 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29737 x : x + (this.unitWidth + this.gutter) * 1,
29742 x : x + (this.unitWidth + this.gutter) * 2,
29750 if(box[0].size == 'xs' && box[1].size == 'xs'){
29759 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29763 x : x + (this.unitWidth + this.gutter) * 1,
29777 x : x + (this.unitWidth + this.gutter) * 2,
29782 x : x + (this.unitWidth + this.gutter) * 2,
29783 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29790 getVerticalFourBoxColPositions : function(x, y, box)
29794 if(box[0].size == 'xs'){
29803 y : y + (this.unitHeight + this.gutter) * 1
29808 y : y + (this.unitHeight + this.gutter) * 2
29812 x : x + (this.unitWidth + this.gutter) * 1,
29826 x : x + (this.unitWidth + this.gutter) * 2,
29831 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29832 y : y + (this.unitHeight + this.gutter) * 1
29836 x : x + (this.unitWidth + this.gutter) * 2,
29837 y : y + (this.unitWidth + this.gutter) * 2
29844 getHorizontalOneBoxColPositions : function(maxX, minY, box)
29848 if(box[0].size == 'md-left'){
29850 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29857 if(box[0].size == 'md-right'){
29859 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29860 y : minY + (this.unitWidth + this.gutter) * 1
29866 var rand = Math.floor(Math.random() * (4 - box[0].y));
29869 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29870 y : minY + (this.unitWidth + this.gutter) * rand
29877 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29881 if(box[0].size == 'xs'){
29884 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29889 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29890 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29898 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29903 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29904 y : minY + (this.unitWidth + this.gutter) * 2
29911 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29915 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29918 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29923 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29924 y : minY + (this.unitWidth + this.gutter) * 1
29928 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29929 y : minY + (this.unitWidth + this.gutter) * 2
29936 if(box[0].size == 'xs' && box[1].size == 'xs'){
29939 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29944 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29949 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29950 y : minY + (this.unitWidth + this.gutter) * 1
29958 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29963 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29964 y : minY + (this.unitWidth + this.gutter) * 2
29968 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29969 y : minY + (this.unitWidth + this.gutter) * 2
29976 getHorizontalFourBoxColPositions : function(maxX, minY, box)
29980 if(box[0].size == 'xs'){
29983 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29988 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29993 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),
29998 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29999 y : minY + (this.unitWidth + this.gutter) * 1
30007 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30012 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30013 y : minY + (this.unitWidth + this.gutter) * 2
30017 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30018 y : minY + (this.unitWidth + this.gutter) * 2
30022 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),
30023 y : minY + (this.unitWidth + this.gutter) * 2
30037 * http://masonry.desandro.com
30039 * The idea is to render all the bricks based on vertical width...
30041 * The original code extends 'outlayer' - we might need to use that....
30047 * @class Roo.bootstrap.LayoutMasonryAuto
30048 * @extends Roo.bootstrap.Component
30049 * Bootstrap Layout Masonry class
30052 * Create a new Element
30053 * @param {Object} config The config object
30056 Roo.bootstrap.LayoutMasonryAuto = function(config){
30057 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30060 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
30063 * @cfg {Boolean} isFitWidth - resize the width..
30065 isFitWidth : false, // options..
30067 * @cfg {Boolean} isOriginLeft = left align?
30069 isOriginLeft : true,
30071 * @cfg {Boolean} isOriginTop = top align?
30073 isOriginTop : false,
30075 * @cfg {Boolean} isLayoutInstant = no animation?
30077 isLayoutInstant : false, // needed?
30079 * @cfg {Boolean} isResizingContainer = not sure if this is used..
30081 isResizingContainer : true,
30083 * @cfg {Number} columnWidth width of the columns
30089 * @cfg {Number} maxCols maximum number of columns
30094 * @cfg {Number} padHeight padding below box..
30100 * @cfg {Boolean} isAutoInitial defalut true
30103 isAutoInitial : true,
30109 initialColumnWidth : 0,
30110 currentSize : null,
30112 colYs : null, // array.
30119 bricks: null, //CompositeElement
30120 cols : 0, // array?
30121 // element : null, // wrapped now this.el
30122 _isLayoutInited : null,
30125 getAutoCreate : function(){
30129 cls: 'blog-masonary-wrapper ' + this.cls,
30131 cls : 'mas-boxes masonary'
30138 getChildContainer: function( )
30140 if (this.boxesEl) {
30141 return this.boxesEl;
30144 this.boxesEl = this.el.select('.mas-boxes').first();
30146 return this.boxesEl;
30150 initEvents : function()
30154 if(this.isAutoInitial){
30155 Roo.log('hook children rendered');
30156 this.on('childrenrendered', function() {
30157 Roo.log('children rendered');
30164 initial : function()
30166 this.reloadItems();
30168 this.currentSize = this.el.getBox(true);
30170 /// was window resize... - let's see if this works..
30171 Roo.EventManager.onWindowResize(this.resize, this);
30173 if(!this.isAutoInitial){
30178 this.layout.defer(500,this);
30181 reloadItems: function()
30183 this.bricks = this.el.select('.masonry-brick', true);
30185 this.bricks.each(function(b) {
30186 //Roo.log(b.getSize());
30187 if (!b.attr('originalwidth')) {
30188 b.attr('originalwidth', b.getSize().width);
30193 Roo.log(this.bricks.elements.length);
30196 resize : function()
30199 var cs = this.el.getBox(true);
30201 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30202 Roo.log("no change in with or X");
30205 this.currentSize = cs;
30209 layout : function()
30212 this._resetLayout();
30213 //this._manageStamps();
30215 // don't animate first layout
30216 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30217 this.layoutItems( isInstant );
30219 // flag for initalized
30220 this._isLayoutInited = true;
30223 layoutItems : function( isInstant )
30225 //var items = this._getItemsForLayout( this.items );
30226 // original code supports filtering layout items.. we just ignore it..
30228 this._layoutItems( this.bricks , isInstant );
30230 this._postLayout();
30232 _layoutItems : function ( items , isInstant)
30234 //this.fireEvent( 'layout', this, items );
30237 if ( !items || !items.elements.length ) {
30238 // no items, emit event with empty array
30243 items.each(function(item) {
30244 Roo.log("layout item");
30246 // get x/y object from method
30247 var position = this._getItemLayoutPosition( item );
30249 position.item = item;
30250 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30251 queue.push( position );
30254 this._processLayoutQueue( queue );
30256 /** Sets position of item in DOM
30257 * @param {Element} item
30258 * @param {Number} x - horizontal position
30259 * @param {Number} y - vertical position
30260 * @param {Boolean} isInstant - disables transitions
30262 _processLayoutQueue : function( queue )
30264 for ( var i=0, len = queue.length; i < len; i++ ) {
30265 var obj = queue[i];
30266 obj.item.position('absolute');
30267 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30273 * Any logic you want to do after each layout,
30274 * i.e. size the container
30276 _postLayout : function()
30278 this.resizeContainer();
30281 resizeContainer : function()
30283 if ( !this.isResizingContainer ) {
30286 var size = this._getContainerSize();
30288 this.el.setSize(size.width,size.height);
30289 this.boxesEl.setSize(size.width,size.height);
30295 _resetLayout : function()
30297 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30298 this.colWidth = this.el.getWidth();
30299 //this.gutter = this.el.getWidth();
30301 this.measureColumns();
30307 this.colYs.push( 0 );
30313 measureColumns : function()
30315 this.getContainerWidth();
30316 // if columnWidth is 0, default to outerWidth of first item
30317 if ( !this.columnWidth ) {
30318 var firstItem = this.bricks.first();
30319 Roo.log(firstItem);
30320 this.columnWidth = this.containerWidth;
30321 if (firstItem && firstItem.attr('originalwidth') ) {
30322 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30324 // columnWidth fall back to item of first element
30325 Roo.log("set column width?");
30326 this.initialColumnWidth = this.columnWidth ;
30328 // if first elem has no width, default to size of container
30333 if (this.initialColumnWidth) {
30334 this.columnWidth = this.initialColumnWidth;
30339 // column width is fixed at the top - however if container width get's smaller we should
30342 // this bit calcs how man columns..
30344 var columnWidth = this.columnWidth += this.gutter;
30346 // calculate columns
30347 var containerWidth = this.containerWidth + this.gutter;
30349 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30350 // fix rounding errors, typically with gutters
30351 var excess = columnWidth - containerWidth % columnWidth;
30354 // if overshoot is less than a pixel, round up, otherwise floor it
30355 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30356 cols = Math[ mathMethod ]( cols );
30357 this.cols = Math.max( cols, 1 );
30358 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30360 // padding positioning..
30361 var totalColWidth = this.cols * this.columnWidth;
30362 var padavail = this.containerWidth - totalColWidth;
30363 // so for 2 columns - we need 3 'pads'
30365 var padNeeded = (1+this.cols) * this.padWidth;
30367 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30369 this.columnWidth += padExtra
30370 //this.padWidth = Math.floor(padavail / ( this.cols));
30372 // adjust colum width so that padding is fixed??
30374 // we have 3 columns ... total = width * 3
30375 // we have X left over... that should be used by
30377 //if (this.expandC) {
30385 getContainerWidth : function()
30387 /* // container is parent if fit width
30388 var container = this.isFitWidth ? this.element.parentNode : this.element;
30389 // check that this.size and size are there
30390 // IE8 triggers resize on body size change, so they might not be
30392 var size = getSize( container ); //FIXME
30393 this.containerWidth = size && size.innerWidth; //FIXME
30396 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30400 _getItemLayoutPosition : function( item ) // what is item?
30402 // we resize the item to our columnWidth..
30404 item.setWidth(this.columnWidth);
30405 item.autoBoxAdjust = false;
30407 var sz = item.getSize();
30409 // how many columns does this brick span
30410 var remainder = this.containerWidth % this.columnWidth;
30412 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30413 // round if off by 1 pixel, otherwise use ceil
30414 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30415 colSpan = Math.min( colSpan, this.cols );
30417 // normally this should be '1' as we dont' currently allow multi width columns..
30419 var colGroup = this._getColGroup( colSpan );
30420 // get the minimum Y value from the columns
30421 var minimumY = Math.min.apply( Math, colGroup );
30422 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30424 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30426 // position the brick
30428 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30429 y: this.currentSize.y + minimumY + this.padHeight
30433 // apply setHeight to necessary columns
30434 var setHeight = minimumY + sz.height + this.padHeight;
30435 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30437 var setSpan = this.cols + 1 - colGroup.length;
30438 for ( var i = 0; i < setSpan; i++ ) {
30439 this.colYs[ shortColIndex + i ] = setHeight ;
30446 * @param {Number} colSpan - number of columns the element spans
30447 * @returns {Array} colGroup
30449 _getColGroup : function( colSpan )
30451 if ( colSpan < 2 ) {
30452 // if brick spans only one column, use all the column Ys
30457 // how many different places could this brick fit horizontally
30458 var groupCount = this.cols + 1 - colSpan;
30459 // for each group potential horizontal position
30460 for ( var i = 0; i < groupCount; i++ ) {
30461 // make an array of colY values for that one group
30462 var groupColYs = this.colYs.slice( i, i + colSpan );
30463 // and get the max value of the array
30464 colGroup[i] = Math.max.apply( Math, groupColYs );
30469 _manageStamp : function( stamp )
30471 var stampSize = stamp.getSize();
30472 var offset = stamp.getBox();
30473 // get the columns that this stamp affects
30474 var firstX = this.isOriginLeft ? offset.x : offset.right;
30475 var lastX = firstX + stampSize.width;
30476 var firstCol = Math.floor( firstX / this.columnWidth );
30477 firstCol = Math.max( 0, firstCol );
30479 var lastCol = Math.floor( lastX / this.columnWidth );
30480 // lastCol should not go over if multiple of columnWidth #425
30481 lastCol -= lastX % this.columnWidth ? 0 : 1;
30482 lastCol = Math.min( this.cols - 1, lastCol );
30484 // set colYs to bottom of the stamp
30485 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30488 for ( var i = firstCol; i <= lastCol; i++ ) {
30489 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30494 _getContainerSize : function()
30496 this.maxY = Math.max.apply( Math, this.colYs );
30501 if ( this.isFitWidth ) {
30502 size.width = this._getContainerFitWidth();
30508 _getContainerFitWidth : function()
30510 var unusedCols = 0;
30511 // count unused columns
30514 if ( this.colYs[i] !== 0 ) {
30519 // fit container to columns that have been used
30520 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30523 needsResizeLayout : function()
30525 var previousWidth = this.containerWidth;
30526 this.getContainerWidth();
30527 return previousWidth !== this.containerWidth;
30542 * @class Roo.bootstrap.MasonryBrick
30543 * @extends Roo.bootstrap.Component
30544 * Bootstrap MasonryBrick class
30547 * Create a new MasonryBrick
30548 * @param {Object} config The config object
30551 Roo.bootstrap.MasonryBrick = function(config){
30552 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30558 * When a MasonryBrick is clcik
30559 * @param {Roo.bootstrap.MasonryBrick} this
30560 * @param {Roo.EventObject} e
30566 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
30569 * @cfg {String} title
30573 * @cfg {String} html
30577 * @cfg {String} bgimage
30581 * @cfg {String} videourl
30585 * @cfg {String} cls
30589 * @cfg {String} href
30593 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30598 * @cfg {String} (center|bottom) placetitle
30602 getAutoCreate : function()
30604 var cls = 'masonry-brick';
30606 if(this.href.length){
30607 cls += ' masonry-brick-link';
30610 if(this.bgimage.length){
30611 cls += ' masonry-brick-image';
30615 cls += ' masonry-' + this.size + '-brick';
30618 if(this.placetitle.length){
30620 switch (this.placetitle) {
30622 cls += ' masonry-center-title';
30625 cls += ' masonry-bottom-title';
30632 if(!this.html.length && !this.bgimage.length){
30633 cls += ' masonry-center-title';
30636 if(!this.html.length && this.bgimage.length){
30637 cls += ' masonry-bottom-title';
30642 cls += ' ' + this.cls;
30646 tag: (this.href.length) ? 'a' : 'div',
30651 cls: 'masonry-brick-paragraph',
30657 if(this.href.length){
30658 cfg.href = this.href;
30661 var cn = cfg.cn[0].cn;
30663 if(this.title.length){
30666 cls: 'masonry-brick-title',
30671 if(this.html.length){
30674 cls: 'masonry-brick-text',
30678 if (!this.title.length && !this.html.length) {
30679 cfg.cn[0].cls += ' hide';
30682 if(this.bgimage.length){
30685 cls: 'masonry-brick-image-view',
30689 if(this.videourl.length){
30690 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30691 // youtube support only?
30694 cls: 'masonry-brick-image-view',
30697 allowfullscreen : true
30706 initEvents: function()
30708 switch (this.size) {
30710 // this.intSize = 1;
30715 // this.intSize = 2;
30722 // this.intSize = 3;
30727 // this.intSize = 3;
30732 // this.intSize = 3;
30737 // this.intSize = 3;
30749 this.el.on('touchstart', this.onTouchStart, this);
30750 this.el.on('touchmove', this.onTouchMove, this);
30751 this.el.on('touchend', this.onTouchEnd, this);
30752 this.el.on('contextmenu', this.onContextMenu, this);
30754 this.el.on('mouseenter' ,this.enter, this);
30755 this.el.on('mouseleave', this.leave, this);
30758 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30759 this.parent().bricks.push(this);
30764 onClick: function(e, el)
30770 var time = this.endTimer - this.startTimer;
30778 e.preventDefault();
30781 enter: function(e, el)
30783 e.preventDefault();
30785 if(this.bgimage.length && this.html.length){
30786 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30790 leave: function(e, el)
30792 e.preventDefault();
30794 if(this.bgimage.length && this.html.length){
30795 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30799 onTouchStart: function(e, el)
30801 // e.preventDefault();
30803 this.touchmoved = false;
30805 if(!this.bgimage.length || !this.html.length){
30809 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30811 this.timer = new Date().getTime();
30815 onTouchMove: function(e, el)
30817 this.touchmoved = true;
30820 onContextMenu : function(e,el)
30822 e.preventDefault();
30823 e.stopPropagation();
30827 onTouchEnd: function(e, el)
30829 // e.preventDefault();
30831 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30838 if(!this.bgimage.length || !this.html.length){
30840 if(this.href.length){
30841 window.location.href = this.href;
30847 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30849 window.location.href = this.href;
30864 * @class Roo.bootstrap.Brick
30865 * @extends Roo.bootstrap.Component
30866 * Bootstrap Brick class
30869 * Create a new Brick
30870 * @param {Object} config The config object
30873 Roo.bootstrap.Brick = function(config){
30874 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30880 * When a Brick is click
30881 * @param {Roo.bootstrap.Brick} this
30882 * @param {Roo.EventObject} e
30888 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
30891 * @cfg {String} title
30895 * @cfg {String} html
30899 * @cfg {String} bgimage
30903 * @cfg {String} cls
30907 * @cfg {String} href
30911 * @cfg {String} video
30915 * @cfg {Boolean} square
30919 getAutoCreate : function()
30921 var cls = 'roo-brick';
30923 if(this.href.length){
30924 cls += ' roo-brick-link';
30927 if(this.bgimage.length){
30928 cls += ' roo-brick-image';
30931 if(!this.html.length && !this.bgimage.length){
30932 cls += ' roo-brick-center-title';
30935 if(!this.html.length && this.bgimage.length){
30936 cls += ' roo-brick-bottom-title';
30940 cls += ' ' + this.cls;
30944 tag: (this.href.length) ? 'a' : 'div',
30949 cls: 'roo-brick-paragraph',
30955 if(this.href.length){
30956 cfg.href = this.href;
30959 var cn = cfg.cn[0].cn;
30961 if(this.title.length){
30964 cls: 'roo-brick-title',
30969 if(this.html.length){
30972 cls: 'roo-brick-text',
30979 if(this.bgimage.length){
30982 cls: 'roo-brick-image-view',
30990 initEvents: function()
30992 if(this.title.length || this.html.length){
30993 this.el.on('mouseenter' ,this.enter, this);
30994 this.el.on('mouseleave', this.leave, this);
30998 Roo.EventManager.onWindowResize(this.resize, this);
31003 resize : function()
31005 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31007 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31008 // paragraph.setHeight(paragraph.getWidth());
31010 if(this.bgimage.length){
31011 var image = this.el.select('.roo-brick-image-view', true).first();
31012 image.setWidth(paragraph.getWidth());
31013 image.setHeight(paragraph.getWidth());
31018 enter: function(e, el)
31020 e.preventDefault();
31022 if(this.bgimage.length){
31023 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31024 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31028 leave: function(e, el)
31030 e.preventDefault();
31032 if(this.bgimage.length){
31033 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31034 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31044 * Ext JS Library 1.1.1
31045 * Copyright(c) 2006-2007, Ext JS, LLC.
31047 * Originally Released Under LGPL - original licence link has changed is not relivant.
31050 * <script type="text/javascript">
31055 * @class Roo.bootstrap.SplitBar
31056 * @extends Roo.util.Observable
31057 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31061 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31062 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31063 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31064 split.minSize = 100;
31065 split.maxSize = 600;
31066 split.animate = true;
31067 split.on('moved', splitterMoved);
31070 * Create a new SplitBar
31071 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
31072 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
31073 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31074 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
31075 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31076 position of the SplitBar).
31078 Roo.bootstrap.SplitBar = function(cfg){
31083 // dragElement : elm
31084 // resizingElement: el,
31086 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31087 // placement : Roo.bootstrap.SplitBar.LEFT ,
31088 // existingProxy ???
31091 this.el = Roo.get(cfg.dragElement, true);
31092 this.el.dom.unselectable = "on";
31094 this.resizingEl = Roo.get(cfg.resizingElement, true);
31098 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31099 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31102 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31105 * The minimum size of the resizing element. (Defaults to 0)
31111 * The maximum size of the resizing element. (Defaults to 2000)
31114 this.maxSize = 2000;
31117 * Whether to animate the transition to the new size
31120 this.animate = false;
31123 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31126 this.useShim = false;
31131 if(!cfg.existingProxy){
31133 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31135 this.proxy = Roo.get(cfg.existingProxy).dom;
31138 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31141 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31144 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31147 this.dragSpecs = {};
31150 * @private The adapter to use to positon and resize elements
31152 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31153 this.adapter.init(this);
31155 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31157 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31158 this.el.addClass("roo-splitbar-h");
31161 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31162 this.el.addClass("roo-splitbar-v");
31168 * Fires when the splitter is moved (alias for {@link #event-moved})
31169 * @param {Roo.bootstrap.SplitBar} this
31170 * @param {Number} newSize the new width or height
31175 * Fires when the splitter is moved
31176 * @param {Roo.bootstrap.SplitBar} this
31177 * @param {Number} newSize the new width or height
31181 * @event beforeresize
31182 * Fires before the splitter is dragged
31183 * @param {Roo.bootstrap.SplitBar} this
31185 "beforeresize" : true,
31187 "beforeapply" : true
31190 Roo.util.Observable.call(this);
31193 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31194 onStartProxyDrag : function(x, y){
31195 this.fireEvent("beforeresize", this);
31197 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
31199 o.enableDisplayMode("block");
31200 // all splitbars share the same overlay
31201 Roo.bootstrap.SplitBar.prototype.overlay = o;
31203 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31204 this.overlay.show();
31205 Roo.get(this.proxy).setDisplayed("block");
31206 var size = this.adapter.getElementSize(this);
31207 this.activeMinSize = this.getMinimumSize();;
31208 this.activeMaxSize = this.getMaximumSize();;
31209 var c1 = size - this.activeMinSize;
31210 var c2 = Math.max(this.activeMaxSize - size, 0);
31211 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31212 this.dd.resetConstraints();
31213 this.dd.setXConstraint(
31214 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
31215 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31217 this.dd.setYConstraint(0, 0);
31219 this.dd.resetConstraints();
31220 this.dd.setXConstraint(0, 0);
31221 this.dd.setYConstraint(
31222 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
31223 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31226 this.dragSpecs.startSize = size;
31227 this.dragSpecs.startPoint = [x, y];
31228 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31232 * @private Called after the drag operation by the DDProxy
31234 onEndProxyDrag : function(e){
31235 Roo.get(this.proxy).setDisplayed(false);
31236 var endPoint = Roo.lib.Event.getXY(e);
31238 this.overlay.hide();
31241 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31242 newSize = this.dragSpecs.startSize +
31243 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31244 endPoint[0] - this.dragSpecs.startPoint[0] :
31245 this.dragSpecs.startPoint[0] - endPoint[0]
31248 newSize = this.dragSpecs.startSize +
31249 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31250 endPoint[1] - this.dragSpecs.startPoint[1] :
31251 this.dragSpecs.startPoint[1] - endPoint[1]
31254 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31255 if(newSize != this.dragSpecs.startSize){
31256 if(this.fireEvent('beforeapply', this, newSize) !== false){
31257 this.adapter.setElementSize(this, newSize);
31258 this.fireEvent("moved", this, newSize);
31259 this.fireEvent("resize", this, newSize);
31265 * Get the adapter this SplitBar uses
31266 * @return The adapter object
31268 getAdapter : function(){
31269 return this.adapter;
31273 * Set the adapter this SplitBar uses
31274 * @param {Object} adapter A SplitBar adapter object
31276 setAdapter : function(adapter){
31277 this.adapter = adapter;
31278 this.adapter.init(this);
31282 * Gets the minimum size for the resizing element
31283 * @return {Number} The minimum size
31285 getMinimumSize : function(){
31286 return this.minSize;
31290 * Sets the minimum size for the resizing element
31291 * @param {Number} minSize The minimum size
31293 setMinimumSize : function(minSize){
31294 this.minSize = minSize;
31298 * Gets the maximum size for the resizing element
31299 * @return {Number} The maximum size
31301 getMaximumSize : function(){
31302 return this.maxSize;
31306 * Sets the maximum size for the resizing element
31307 * @param {Number} maxSize The maximum size
31309 setMaximumSize : function(maxSize){
31310 this.maxSize = maxSize;
31314 * Sets the initialize size for the resizing element
31315 * @param {Number} size The initial size
31317 setCurrentSize : function(size){
31318 var oldAnimate = this.animate;
31319 this.animate = false;
31320 this.adapter.setElementSize(this, size);
31321 this.animate = oldAnimate;
31325 * Destroy this splitbar.
31326 * @param {Boolean} removeEl True to remove the element
31328 destroy : function(removeEl){
31330 this.shim.remove();
31333 this.proxy.parentNode.removeChild(this.proxy);
31341 * @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.
31343 Roo.bootstrap.SplitBar.createProxy = function(dir){
31344 var proxy = new Roo.Element(document.createElement("div"));
31345 proxy.unselectable();
31346 var cls = 'roo-splitbar-proxy';
31347 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31348 document.body.appendChild(proxy.dom);
31353 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31354 * Default Adapter. It assumes the splitter and resizing element are not positioned
31355 * elements and only gets/sets the width of the element. Generally used for table based layouts.
31357 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31360 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31361 // do nothing for now
31362 init : function(s){
31366 * Called before drag operations to get the current size of the resizing element.
31367 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31369 getElementSize : function(s){
31370 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31371 return s.resizingEl.getWidth();
31373 return s.resizingEl.getHeight();
31378 * Called after drag operations to set the size of the resizing element.
31379 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31380 * @param {Number} newSize The new size to set
31381 * @param {Function} onComplete A function to be invoked when resizing is complete
31383 setElementSize : function(s, newSize, onComplete){
31384 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31386 s.resizingEl.setWidth(newSize);
31388 onComplete(s, newSize);
31391 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31396 s.resizingEl.setHeight(newSize);
31398 onComplete(s, newSize);
31401 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31408 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31409 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31410 * Adapter that moves the splitter element to align with the resized sizing element.
31411 * Used with an absolute positioned SplitBar.
31412 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31413 * document.body, make sure you assign an id to the body element.
31415 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31416 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31417 this.container = Roo.get(container);
31420 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31421 init : function(s){
31422 this.basic.init(s);
31425 getElementSize : function(s){
31426 return this.basic.getElementSize(s);
31429 setElementSize : function(s, newSize, onComplete){
31430 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31433 moveSplitter : function(s){
31434 var yes = Roo.bootstrap.SplitBar;
31435 switch(s.placement){
31437 s.el.setX(s.resizingEl.getRight());
31440 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31443 s.el.setY(s.resizingEl.getBottom());
31446 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31453 * Orientation constant - Create a vertical SplitBar
31457 Roo.bootstrap.SplitBar.VERTICAL = 1;
31460 * Orientation constant - Create a horizontal SplitBar
31464 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31467 * Placement constant - The resizing element is to the left of the splitter element
31471 Roo.bootstrap.SplitBar.LEFT = 1;
31474 * Placement constant - The resizing element is to the right of the splitter element
31478 Roo.bootstrap.SplitBar.RIGHT = 2;
31481 * Placement constant - The resizing element is positioned above the splitter element
31485 Roo.bootstrap.SplitBar.TOP = 3;
31488 * Placement constant - The resizing element is positioned under splitter element
31492 Roo.bootstrap.SplitBar.BOTTOM = 4;
31493 Roo.namespace("Roo.bootstrap.layout");/*
31495 * Ext JS Library 1.1.1
31496 * Copyright(c) 2006-2007, Ext JS, LLC.
31498 * Originally Released Under LGPL - original licence link has changed is not relivant.
31501 * <script type="text/javascript">
31505 * @class Roo.bootstrap.layout.Manager
31506 * @extends Roo.bootstrap.Component
31507 * Base class for layout managers.
31509 Roo.bootstrap.layout.Manager = function(config)
31511 Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31517 /** false to disable window resize monitoring @type Boolean */
31518 this.monitorWindowResize = true;
31523 * Fires when a layout is performed.
31524 * @param {Roo.LayoutManager} this
31528 * @event regionresized
31529 * Fires when the user resizes a region.
31530 * @param {Roo.LayoutRegion} region The resized region
31531 * @param {Number} newSize The new size (width for east/west, height for north/south)
31533 "regionresized" : true,
31535 * @event regioncollapsed
31536 * Fires when a region is collapsed.
31537 * @param {Roo.LayoutRegion} region The collapsed region
31539 "regioncollapsed" : true,
31541 * @event regionexpanded
31542 * Fires when a region is expanded.
31543 * @param {Roo.LayoutRegion} region The expanded region
31545 "regionexpanded" : true
31547 this.updating = false;
31550 this.el = Roo.get(config.el);
31556 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31561 monitorWindowResize : true,
31567 onRender : function(ct, position)
31570 this.el = Roo.get(ct);
31576 initEvents: function()
31580 // ie scrollbar fix
31581 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31582 document.body.scroll = "no";
31583 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31584 this.el.position('relative');
31586 this.id = this.el.id;
31587 this.el.addClass("roo-layout-container");
31588 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31589 if(this.el.dom != document.body ) {
31590 this.el.on('resize', this.layout,this);
31591 this.el.on('show', this.layout,this);
31597 * Returns true if this layout is currently being updated
31598 * @return {Boolean}
31600 isUpdating : function(){
31601 return this.updating;
31605 * Suspend the LayoutManager from doing auto-layouts while
31606 * making multiple add or remove calls
31608 beginUpdate : function(){
31609 this.updating = true;
31613 * Restore auto-layouts and optionally disable the manager from performing a layout
31614 * @param {Boolean} noLayout true to disable a layout update
31616 endUpdate : function(noLayout){
31617 this.updating = false;
31623 layout: function(){
31627 onRegionResized : function(region, newSize){
31628 this.fireEvent("regionresized", region, newSize);
31632 onRegionCollapsed : function(region){
31633 this.fireEvent("regioncollapsed", region);
31636 onRegionExpanded : function(region){
31637 this.fireEvent("regionexpanded", region);
31641 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31642 * performs box-model adjustments.
31643 * @return {Object} The size as an object {width: (the width), height: (the height)}
31645 getViewSize : function()
31648 if(this.el.dom != document.body){
31649 size = this.el.getSize();
31651 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31653 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31654 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31659 * Returns the Element this layout is bound to.
31660 * @return {Roo.Element}
31662 getEl : function(){
31667 * Returns the specified region.
31668 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31669 * @return {Roo.LayoutRegion}
31671 getRegion : function(target){
31672 return this.regions[target.toLowerCase()];
31675 onWindowResize : function(){
31676 if(this.monitorWindowResize){
31682 * Ext JS Library 1.1.1
31683 * Copyright(c) 2006-2007, Ext JS, LLC.
31685 * Originally Released Under LGPL - original licence link has changed is not relivant.
31688 * <script type="text/javascript">
31691 * @class Roo.bootstrap.layout.Border
31692 * @extends Roo.bootstrap.layout.Manager
31693 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31694 * please see: examples/bootstrap/nested.html<br><br>
31696 <b>The container the layout is rendered into can be either the body element or any other element.
31697 If it is not the body element, the container needs to either be an absolute positioned element,
31698 or you will need to add "position:relative" to the css of the container. You will also need to specify
31699 the container size if it is not the body element.</b>
31702 * Create a new Border
31703 * @param {Object} config Configuration options
31705 Roo.bootstrap.layout.Border = function(config){
31706 config = config || {};
31707 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31711 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31712 if(config[region]){
31713 config[region].region = region;
31714 this.addRegion(config[region]);
31720 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
31722 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31724 * Creates and adds a new region if it doesn't already exist.
31725 * @param {String} target The target region key (north, south, east, west or center).
31726 * @param {Object} config The regions config object
31727 * @return {BorderLayoutRegion} The new region
31729 addRegion : function(config)
31731 if(!this.regions[config.region]){
31732 var r = this.factory(config);
31733 this.bindRegion(r);
31735 return this.regions[config.region];
31739 bindRegion : function(r){
31740 this.regions[r.config.region] = r;
31742 r.on("visibilitychange", this.layout, this);
31743 r.on("paneladded", this.layout, this);
31744 r.on("panelremoved", this.layout, this);
31745 r.on("invalidated", this.layout, this);
31746 r.on("resized", this.onRegionResized, this);
31747 r.on("collapsed", this.onRegionCollapsed, this);
31748 r.on("expanded", this.onRegionExpanded, this);
31752 * Performs a layout update.
31754 layout : function()
31756 if(this.updating) {
31760 // render all the rebions if they have not been done alreayd?
31761 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31762 if(this.regions[region] && !this.regions[region].bodyEl){
31763 this.regions[region].onRender(this.el)
31767 var size = this.getViewSize();
31768 var w = size.width;
31769 var h = size.height;
31774 //var x = 0, y = 0;
31776 var rs = this.regions;
31777 var north = rs["north"];
31778 var south = rs["south"];
31779 var west = rs["west"];
31780 var east = rs["east"];
31781 var center = rs["center"];
31782 //if(this.hideOnLayout){ // not supported anymore
31783 //c.el.setStyle("display", "none");
31785 if(north && north.isVisible()){
31786 var b = north.getBox();
31787 var m = north.getMargins();
31788 b.width = w - (m.left+m.right);
31791 centerY = b.height + b.y + m.bottom;
31792 centerH -= centerY;
31793 north.updateBox(this.safeBox(b));
31795 if(south && south.isVisible()){
31796 var b = south.getBox();
31797 var m = south.getMargins();
31798 b.width = w - (m.left+m.right);
31800 var totalHeight = (b.height + m.top + m.bottom);
31801 b.y = h - totalHeight + m.top;
31802 centerH -= totalHeight;
31803 south.updateBox(this.safeBox(b));
31805 if(west && west.isVisible()){
31806 var b = west.getBox();
31807 var m = west.getMargins();
31808 b.height = centerH - (m.top+m.bottom);
31810 b.y = centerY + m.top;
31811 var totalWidth = (b.width + m.left + m.right);
31812 centerX += totalWidth;
31813 centerW -= totalWidth;
31814 west.updateBox(this.safeBox(b));
31816 if(east && east.isVisible()){
31817 var b = east.getBox();
31818 var m = east.getMargins();
31819 b.height = centerH - (m.top+m.bottom);
31820 var totalWidth = (b.width + m.left + m.right);
31821 b.x = w - totalWidth + m.left;
31822 b.y = centerY + m.top;
31823 centerW -= totalWidth;
31824 east.updateBox(this.safeBox(b));
31827 var m = center.getMargins();
31829 x: centerX + m.left,
31830 y: centerY + m.top,
31831 width: centerW - (m.left+m.right),
31832 height: centerH - (m.top+m.bottom)
31834 //if(this.hideOnLayout){
31835 //center.el.setStyle("display", "block");
31837 center.updateBox(this.safeBox(centerBox));
31840 this.fireEvent("layout", this);
31844 safeBox : function(box){
31845 box.width = Math.max(0, box.width);
31846 box.height = Math.max(0, box.height);
31851 * Adds a ContentPanel (or subclass) to this layout.
31852 * @param {String} target The target region key (north, south, east, west or center).
31853 * @param {Roo.ContentPanel} panel The panel to add
31854 * @return {Roo.ContentPanel} The added panel
31856 add : function(target, panel){
31858 target = target.toLowerCase();
31859 return this.regions[target].add(panel);
31863 * Remove a ContentPanel (or subclass) to this layout.
31864 * @param {String} target The target region key (north, south, east, west or center).
31865 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31866 * @return {Roo.ContentPanel} The removed panel
31868 remove : function(target, panel){
31869 target = target.toLowerCase();
31870 return this.regions[target].remove(panel);
31874 * Searches all regions for a panel with the specified id
31875 * @param {String} panelId
31876 * @return {Roo.ContentPanel} The panel or null if it wasn't found
31878 findPanel : function(panelId){
31879 var rs = this.regions;
31880 for(var target in rs){
31881 if(typeof rs[target] != "function"){
31882 var p = rs[target].getPanel(panelId);
31892 * Searches all regions for a panel with the specified id and activates (shows) it.
31893 * @param {String/ContentPanel} panelId The panels id or the panel itself
31894 * @return {Roo.ContentPanel} The shown panel or null
31896 showPanel : function(panelId) {
31897 var rs = this.regions;
31898 for(var target in rs){
31899 var r = rs[target];
31900 if(typeof r != "function"){
31901 if(r.hasPanel(panelId)){
31902 return r.showPanel(panelId);
31910 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31911 * @param {Roo.state.Provider} provider (optional) An alternate state provider
31914 restoreState : function(provider){
31916 provider = Roo.state.Manager;
31918 var sm = new Roo.LayoutStateManager();
31919 sm.init(this, provider);
31925 * Adds a xtype elements to the layout.
31929 xtype : 'ContentPanel',
31936 xtype : 'NestedLayoutPanel',
31942 items : [ ... list of content panels or nested layout panels.. ]
31946 * @param {Object} cfg Xtype definition of item to add.
31948 addxtype : function(cfg)
31950 // basically accepts a pannel...
31951 // can accept a layout region..!?!?
31952 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31955 // theory? children can only be panels??
31957 //if (!cfg.xtype.match(/Panel$/)) {
31962 if (typeof(cfg.region) == 'undefined') {
31963 Roo.log("Failed to add Panel, region was not set");
31967 var region = cfg.region;
31973 xitems = cfg.items;
31980 case 'Content': // ContentPanel (el, cfg)
31981 case 'Scroll': // ContentPanel (el, cfg)
31983 cfg.autoCreate = true;
31984 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31986 // var el = this.el.createChild();
31987 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31990 this.add(region, ret);
31994 case 'TreePanel': // our new panel!
31995 cfg.el = this.el.createChild();
31996 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31997 this.add(region, ret);
32002 // create a new Layout (which is a Border Layout...
32004 var clayout = cfg.layout;
32005 clayout.el = this.el.createChild();
32006 clayout.items = clayout.items || [];
32010 // replace this exitems with the clayout ones..
32011 xitems = clayout.items;
32013 // force background off if it's in center...
32014 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32015 cfg.background = false;
32017 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
32020 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32021 //console.log('adding nested layout panel ' + cfg.toSource());
32022 this.add(region, ret);
32023 nb = {}; /// find first...
32028 // needs grid and region
32030 //var el = this.getRegion(region).el.createChild();
32032 *var el = this.el.createChild();
32033 // create the grid first...
32034 cfg.grid.container = el;
32035 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
32038 if (region == 'center' && this.active ) {
32039 cfg.background = false;
32042 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32044 this.add(region, ret);
32046 if (cfg.background) {
32047 // render grid on panel activation (if panel background)
32048 ret.on('activate', function(gp) {
32049 if (!gp.grid.rendered) {
32050 // gp.grid.render(el);
32054 // cfg.grid.render(el);
32060 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
32061 // it was the old xcomponent building that caused this before.
32062 // espeically if border is the top element in the tree.
32072 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32074 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32075 this.add(region, ret);
32079 throw "Can not add '" + cfg.xtype + "' to Border";
32085 this.beginUpdate();
32089 Roo.each(xitems, function(i) {
32090 region = nb && i.region ? i.region : false;
32092 var add = ret.addxtype(i);
32095 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32096 if (!i.background) {
32097 abn[region] = nb[region] ;
32104 // make the last non-background panel active..
32105 //if (nb) { Roo.log(abn); }
32108 for(var r in abn) {
32109 region = this.getRegion(r);
32111 // tried using nb[r], but it does not work..
32113 region.showPanel(abn[r]);
32124 factory : function(cfg)
32127 var validRegions = Roo.bootstrap.layout.Border.regions;
32129 var target = cfg.region;
32132 var r = Roo.bootstrap.layout;
32136 return new r.North(cfg);
32138 return new r.South(cfg);
32140 return new r.East(cfg);
32142 return new r.West(cfg);
32144 return new r.Center(cfg);
32146 throw 'Layout region "'+target+'" not supported.';
32153 * Ext JS Library 1.1.1
32154 * Copyright(c) 2006-2007, Ext JS, LLC.
32156 * Originally Released Under LGPL - original licence link has changed is not relivant.
32159 * <script type="text/javascript">
32163 * @class Roo.bootstrap.layout.Basic
32164 * @extends Roo.util.Observable
32165 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32166 * and does not have a titlebar, tabs or any other features. All it does is size and position
32167 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32168 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32169 * @cfg {string} region the region that it inhabits..
32170 * @cfg {bool} skipConfig skip config?
32174 Roo.bootstrap.layout.Basic = function(config){
32176 this.mgr = config.mgr;
32178 this.position = config.region;
32180 var skipConfig = config.skipConfig;
32184 * @scope Roo.BasicLayoutRegion
32188 * @event beforeremove
32189 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32190 * @param {Roo.LayoutRegion} this
32191 * @param {Roo.ContentPanel} panel The panel
32192 * @param {Object} e The cancel event object
32194 "beforeremove" : true,
32196 * @event invalidated
32197 * Fires when the layout for this region is changed.
32198 * @param {Roo.LayoutRegion} this
32200 "invalidated" : true,
32202 * @event visibilitychange
32203 * Fires when this region is shown or hidden
32204 * @param {Roo.LayoutRegion} this
32205 * @param {Boolean} visibility true or false
32207 "visibilitychange" : true,
32209 * @event paneladded
32210 * Fires when a panel is added.
32211 * @param {Roo.LayoutRegion} this
32212 * @param {Roo.ContentPanel} panel The panel
32214 "paneladded" : true,
32216 * @event panelremoved
32217 * Fires when a panel is removed.
32218 * @param {Roo.LayoutRegion} this
32219 * @param {Roo.ContentPanel} panel The panel
32221 "panelremoved" : true,
32223 * @event beforecollapse
32224 * Fires when this region before collapse.
32225 * @param {Roo.LayoutRegion} this
32227 "beforecollapse" : true,
32230 * Fires when this region is collapsed.
32231 * @param {Roo.LayoutRegion} this
32233 "collapsed" : true,
32236 * Fires when this region is expanded.
32237 * @param {Roo.LayoutRegion} this
32242 * Fires when this region is slid into view.
32243 * @param {Roo.LayoutRegion} this
32245 "slideshow" : true,
32248 * Fires when this region slides out of view.
32249 * @param {Roo.LayoutRegion} this
32251 "slidehide" : true,
32253 * @event panelactivated
32254 * Fires when a panel is activated.
32255 * @param {Roo.LayoutRegion} this
32256 * @param {Roo.ContentPanel} panel The activated panel
32258 "panelactivated" : true,
32261 * Fires when the user resizes this region.
32262 * @param {Roo.LayoutRegion} this
32263 * @param {Number} newSize The new size (width for east/west, height for north/south)
32267 /** A collection of panels in this region. @type Roo.util.MixedCollection */
32268 this.panels = new Roo.util.MixedCollection();
32269 this.panels.getKey = this.getPanelId.createDelegate(this);
32271 this.activePanel = null;
32272 // ensure listeners are added...
32274 if (config.listeners || config.events) {
32275 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32276 listeners : config.listeners || {},
32277 events : config.events || {}
32281 if(skipConfig !== true){
32282 this.applyConfig(config);
32286 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32288 getPanelId : function(p){
32292 applyConfig : function(config){
32293 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32294 this.config = config;
32299 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
32300 * the width, for horizontal (north, south) the height.
32301 * @param {Number} newSize The new width or height
32303 resizeTo : function(newSize){
32304 var el = this.el ? this.el :
32305 (this.activePanel ? this.activePanel.getEl() : null);
32307 switch(this.position){
32310 el.setWidth(newSize);
32311 this.fireEvent("resized", this, newSize);
32315 el.setHeight(newSize);
32316 this.fireEvent("resized", this, newSize);
32322 getBox : function(){
32323 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32326 getMargins : function(){
32327 return this.margins;
32330 updateBox : function(box){
32332 var el = this.activePanel.getEl();
32333 el.dom.style.left = box.x + "px";
32334 el.dom.style.top = box.y + "px";
32335 this.activePanel.setSize(box.width, box.height);
32339 * Returns the container element for this region.
32340 * @return {Roo.Element}
32342 getEl : function(){
32343 return this.activePanel;
32347 * Returns true if this region is currently visible.
32348 * @return {Boolean}
32350 isVisible : function(){
32351 return this.activePanel ? true : false;
32354 setActivePanel : function(panel){
32355 panel = this.getPanel(panel);
32356 if(this.activePanel && this.activePanel != panel){
32357 this.activePanel.setActiveState(false);
32358 this.activePanel.getEl().setLeftTop(-10000,-10000);
32360 this.activePanel = panel;
32361 panel.setActiveState(true);
32363 panel.setSize(this.box.width, this.box.height);
32365 this.fireEvent("panelactivated", this, panel);
32366 this.fireEvent("invalidated");
32370 * Show the specified panel.
32371 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32372 * @return {Roo.ContentPanel} The shown panel or null
32374 showPanel : function(panel){
32375 panel = this.getPanel(panel);
32377 this.setActivePanel(panel);
32383 * Get the active panel for this region.
32384 * @return {Roo.ContentPanel} The active panel or null
32386 getActivePanel : function(){
32387 return this.activePanel;
32391 * Add the passed ContentPanel(s)
32392 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32393 * @return {Roo.ContentPanel} The panel added (if only one was added)
32395 add : function(panel){
32396 if(arguments.length > 1){
32397 for(var i = 0, len = arguments.length; i < len; i++) {
32398 this.add(arguments[i]);
32402 if(this.hasPanel(panel)){
32403 this.showPanel(panel);
32406 var el = panel.getEl();
32407 if(el.dom.parentNode != this.mgr.el.dom){
32408 this.mgr.el.dom.appendChild(el.dom);
32410 if(panel.setRegion){
32411 panel.setRegion(this);
32413 this.panels.add(panel);
32414 el.setStyle("position", "absolute");
32415 if(!panel.background){
32416 this.setActivePanel(panel);
32417 if(this.config.initialSize && this.panels.getCount()==1){
32418 this.resizeTo(this.config.initialSize);
32421 this.fireEvent("paneladded", this, panel);
32426 * Returns true if the panel is in this region.
32427 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32428 * @return {Boolean}
32430 hasPanel : function(panel){
32431 if(typeof panel == "object"){ // must be panel obj
32432 panel = panel.getId();
32434 return this.getPanel(panel) ? true : false;
32438 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32439 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32440 * @param {Boolean} preservePanel Overrides the config preservePanel option
32441 * @return {Roo.ContentPanel} The panel that was removed
32443 remove : function(panel, preservePanel){
32444 panel = this.getPanel(panel);
32449 this.fireEvent("beforeremove", this, panel, e);
32450 if(e.cancel === true){
32453 var panelId = panel.getId();
32454 this.panels.removeKey(panelId);
32459 * Returns the panel specified or null if it's not in this region.
32460 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32461 * @return {Roo.ContentPanel}
32463 getPanel : function(id){
32464 if(typeof id == "object"){ // must be panel obj
32467 return this.panels.get(id);
32471 * Returns this regions position (north/south/east/west/center).
32474 getPosition: function(){
32475 return this.position;
32479 * Ext JS Library 1.1.1
32480 * Copyright(c) 2006-2007, Ext JS, LLC.
32482 * Originally Released Under LGPL - original licence link has changed is not relivant.
32485 * <script type="text/javascript">
32489 * @class Roo.bootstrap.layout.Region
32490 * @extends Roo.bootstrap.layout.Basic
32491 * This class represents a region in a layout manager.
32493 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32494 * @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})
32495 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
32496 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
32497 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
32498 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
32499 * @cfg {String} title The title for the region (overrides panel titles)
32500 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
32501 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32502 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
32503 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32504 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
32505 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32506 * the space available, similar to FireFox 1.5 tabs (defaults to false)
32507 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
32508 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
32509 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
32511 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
32512 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
32513 * @cfg {Boolean} disableTabTips True to disable tab tooltips
32514 * @cfg {Number} width For East/West panels
32515 * @cfg {Number} height For North/South panels
32516 * @cfg {Boolean} split To show the splitter
32517 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
32519 * @cfg {string} cls Extra CSS classes to add to region
32521 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32522 * @cfg {string} region the region that it inhabits..
32525 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
32526 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
32528 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
32529 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
32530 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
32532 Roo.bootstrap.layout.Region = function(config)
32534 this.applyConfig(config);
32536 var mgr = config.mgr;
32537 var pos = config.region;
32538 config.skipConfig = true;
32539 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32542 this.onRender(mgr.el);
32545 this.visible = true;
32546 this.collapsed = false;
32547 this.unrendered_panels = [];
32550 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32552 position: '', // set by wrapper (eg. north/south etc..)
32553 unrendered_panels : null, // unrendered panels.
32554 createBody : function(){
32555 /** This region's body element
32556 * @type Roo.Element */
32557 this.bodyEl = this.el.createChild({
32559 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32563 onRender: function(ctr, pos)
32565 var dh = Roo.DomHelper;
32566 /** This region's container element
32567 * @type Roo.Element */
32568 this.el = dh.append(ctr.dom, {
32570 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32572 /** This region's title element
32573 * @type Roo.Element */
32575 this.titleEl = dh.append(this.el.dom,
32578 unselectable: "on",
32579 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32581 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
32582 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32585 this.titleEl.enableDisplayMode();
32586 /** This region's title text element
32587 * @type HTMLElement */
32588 this.titleTextEl = this.titleEl.dom.firstChild;
32589 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32591 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32592 this.closeBtn.enableDisplayMode();
32593 this.closeBtn.on("click", this.closeClicked, this);
32594 this.closeBtn.hide();
32596 this.createBody(this.config);
32597 if(this.config.hideWhenEmpty){
32599 this.on("paneladded", this.validateVisibility, this);
32600 this.on("panelremoved", this.validateVisibility, this);
32602 if(this.autoScroll){
32603 this.bodyEl.setStyle("overflow", "auto");
32605 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32607 //if(c.titlebar !== false){
32608 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32609 this.titleEl.hide();
32611 this.titleEl.show();
32612 if(this.config.title){
32613 this.titleTextEl.innerHTML = this.config.title;
32617 if(this.config.collapsed){
32618 this.collapse(true);
32620 if(this.config.hidden){
32624 if (this.unrendered_panels && this.unrendered_panels.length) {
32625 for (var i =0;i< this.unrendered_panels.length; i++) {
32626 this.add(this.unrendered_panels[i]);
32628 this.unrendered_panels = null;
32634 applyConfig : function(c)
32637 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32638 var dh = Roo.DomHelper;
32639 if(c.titlebar !== false){
32640 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32641 this.collapseBtn.on("click", this.collapse, this);
32642 this.collapseBtn.enableDisplayMode();
32644 if(c.showPin === true || this.showPin){
32645 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32646 this.stickBtn.enableDisplayMode();
32647 this.stickBtn.on("click", this.expand, this);
32648 this.stickBtn.hide();
32653 /** This region's collapsed element
32654 * @type Roo.Element */
32657 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32658 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32661 if(c.floatable !== false){
32662 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32663 this.collapsedEl.on("click", this.collapseClick, this);
32666 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32667 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32668 id: "message", unselectable: "on", style:{"float":"left"}});
32669 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32671 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32672 this.expandBtn.on("click", this.expand, this);
32676 if(this.collapseBtn){
32677 this.collapseBtn.setVisible(c.collapsible == true);
32680 this.cmargins = c.cmargins || this.cmargins ||
32681 (this.position == "west" || this.position == "east" ?
32682 {top: 0, left: 2, right:2, bottom: 0} :
32683 {top: 2, left: 0, right:0, bottom: 2});
32685 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32688 this.bottomTabs = c.tabPosition != "top";
32690 this.autoScroll = c.autoScroll || false;
32695 this.duration = c.duration || .30;
32696 this.slideDuration = c.slideDuration || .45;
32701 * Returns true if this region is currently visible.
32702 * @return {Boolean}
32704 isVisible : function(){
32705 return this.visible;
32709 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32710 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
32712 //setCollapsedTitle : function(title){
32713 // title = title || " ";
32714 // if(this.collapsedTitleTextEl){
32715 // this.collapsedTitleTextEl.innerHTML = title;
32719 getBox : function(){
32721 // if(!this.collapsed){
32722 b = this.el.getBox(false, true);
32724 // b = this.collapsedEl.getBox(false, true);
32729 getMargins : function(){
32730 return this.margins;
32731 //return this.collapsed ? this.cmargins : this.margins;
32734 highlight : function(){
32735 this.el.addClass("x-layout-panel-dragover");
32738 unhighlight : function(){
32739 this.el.removeClass("x-layout-panel-dragover");
32742 updateBox : function(box)
32744 if (!this.bodyEl) {
32745 return; // not rendered yet..
32749 if(!this.collapsed){
32750 this.el.dom.style.left = box.x + "px";
32751 this.el.dom.style.top = box.y + "px";
32752 this.updateBody(box.width, box.height);
32754 this.collapsedEl.dom.style.left = box.x + "px";
32755 this.collapsedEl.dom.style.top = box.y + "px";
32756 this.collapsedEl.setSize(box.width, box.height);
32759 this.tabs.autoSizeTabs();
32763 updateBody : function(w, h)
32766 this.el.setWidth(w);
32767 w -= this.el.getBorderWidth("rl");
32768 if(this.config.adjustments){
32769 w += this.config.adjustments[0];
32773 this.el.setHeight(h);
32774 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32775 h -= this.el.getBorderWidth("tb");
32776 if(this.config.adjustments){
32777 h += this.config.adjustments[1];
32779 this.bodyEl.setHeight(h);
32781 h = this.tabs.syncHeight(h);
32784 if(this.panelSize){
32785 w = w !== null ? w : this.panelSize.width;
32786 h = h !== null ? h : this.panelSize.height;
32788 if(this.activePanel){
32789 var el = this.activePanel.getEl();
32790 w = w !== null ? w : el.getWidth();
32791 h = h !== null ? h : el.getHeight();
32792 this.panelSize = {width: w, height: h};
32793 this.activePanel.setSize(w, h);
32795 if(Roo.isIE && this.tabs){
32796 this.tabs.el.repaint();
32801 * Returns the container element for this region.
32802 * @return {Roo.Element}
32804 getEl : function(){
32809 * Hides this region.
32812 //if(!this.collapsed){
32813 this.el.dom.style.left = "-2000px";
32816 // this.collapsedEl.dom.style.left = "-2000px";
32817 // this.collapsedEl.hide();
32819 this.visible = false;
32820 this.fireEvent("visibilitychange", this, false);
32824 * Shows this region if it was previously hidden.
32827 //if(!this.collapsed){
32830 // this.collapsedEl.show();
32832 this.visible = true;
32833 this.fireEvent("visibilitychange", this, true);
32836 closeClicked : function(){
32837 if(this.activePanel){
32838 this.remove(this.activePanel);
32842 collapseClick : function(e){
32844 e.stopPropagation();
32847 e.stopPropagation();
32853 * Collapses this region.
32854 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32857 collapse : function(skipAnim, skipCheck = false){
32858 if(this.collapsed) {
32862 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32864 this.collapsed = true;
32866 this.split.el.hide();
32868 if(this.config.animate && skipAnim !== true){
32869 this.fireEvent("invalidated", this);
32870 this.animateCollapse();
32872 this.el.setLocation(-20000,-20000);
32874 this.collapsedEl.show();
32875 this.fireEvent("collapsed", this);
32876 this.fireEvent("invalidated", this);
32882 animateCollapse : function(){
32887 * Expands this region if it was previously collapsed.
32888 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32889 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32892 expand : function(e, skipAnim){
32894 e.stopPropagation();
32896 if(!this.collapsed || this.el.hasActiveFx()) {
32900 this.afterSlideIn();
32903 this.collapsed = false;
32904 if(this.config.animate && skipAnim !== true){
32905 this.animateExpand();
32909 this.split.el.show();
32911 this.collapsedEl.setLocation(-2000,-2000);
32912 this.collapsedEl.hide();
32913 this.fireEvent("invalidated", this);
32914 this.fireEvent("expanded", this);
32918 animateExpand : function(){
32922 initTabs : function()
32924 this.bodyEl.setStyle("overflow", "hidden");
32925 var ts = new Roo.bootstrap.panel.Tabs({
32926 el: this.bodyEl.dom,
32927 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32928 disableTooltips: this.config.disableTabTips,
32929 toolbar : this.config.toolbar
32932 if(this.config.hideTabs){
32933 ts.stripWrap.setDisplayed(false);
32936 ts.resizeTabs = this.config.resizeTabs === true;
32937 ts.minTabWidth = this.config.minTabWidth || 40;
32938 ts.maxTabWidth = this.config.maxTabWidth || 250;
32939 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32940 ts.monitorResize = false;
32941 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32942 ts.bodyEl.addClass('roo-layout-tabs-body');
32943 this.panels.each(this.initPanelAsTab, this);
32946 initPanelAsTab : function(panel){
32947 var ti = this.tabs.addTab(
32951 this.config.closeOnTab && panel.isClosable()
32953 if(panel.tabTip !== undefined){
32954 ti.setTooltip(panel.tabTip);
32956 ti.on("activate", function(){
32957 this.setActivePanel(panel);
32960 if(this.config.closeOnTab){
32961 ti.on("beforeclose", function(t, e){
32963 this.remove(panel);
32969 updatePanelTitle : function(panel, title)
32971 if(this.activePanel == panel){
32972 this.updateTitle(title);
32975 var ti = this.tabs.getTab(panel.getEl().id);
32977 if(panel.tabTip !== undefined){
32978 ti.setTooltip(panel.tabTip);
32983 updateTitle : function(title){
32984 if(this.titleTextEl && !this.config.title){
32985 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
32989 setActivePanel : function(panel)
32991 panel = this.getPanel(panel);
32992 if(this.activePanel && this.activePanel != panel){
32993 this.activePanel.setActiveState(false);
32995 this.activePanel = panel;
32996 panel.setActiveState(true);
32997 if(this.panelSize){
32998 panel.setSize(this.panelSize.width, this.panelSize.height);
33001 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33003 this.updateTitle(panel.getTitle());
33005 this.fireEvent("invalidated", this);
33007 this.fireEvent("panelactivated", this, panel);
33011 * Shows the specified panel.
33012 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33013 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33015 showPanel : function(panel)
33017 panel = this.getPanel(panel);
33020 var tab = this.tabs.getTab(panel.getEl().id);
33021 if(tab.isHidden()){
33022 this.tabs.unhideTab(tab.id);
33026 this.setActivePanel(panel);
33033 * Get the active panel for this region.
33034 * @return {Roo.ContentPanel} The active panel or null
33036 getActivePanel : function(){
33037 return this.activePanel;
33040 validateVisibility : function(){
33041 if(this.panels.getCount() < 1){
33042 this.updateTitle(" ");
33043 this.closeBtn.hide();
33046 if(!this.isVisible()){
33053 * Adds the passed ContentPanel(s) to this region.
33054 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33055 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33057 add : function(panel)
33059 if(arguments.length > 1){
33060 for(var i = 0, len = arguments.length; i < len; i++) {
33061 this.add(arguments[i]);
33066 // if we have not been rendered yet, then we can not really do much of this..
33067 if (!this.bodyEl) {
33068 this.unrendered_panels.push(panel);
33075 if(this.hasPanel(panel)){
33076 this.showPanel(panel);
33079 panel.setRegion(this);
33080 this.panels.add(panel);
33081 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33082 // sinle panel - no tab...?? would it not be better to render it with the tabs,
33083 // and hide them... ???
33084 this.bodyEl.dom.appendChild(panel.getEl().dom);
33085 if(panel.background !== true){
33086 this.setActivePanel(panel);
33088 this.fireEvent("paneladded", this, panel);
33095 this.initPanelAsTab(panel);
33099 if(panel.background !== true){
33100 this.tabs.activate(panel.getEl().id);
33102 this.fireEvent("paneladded", this, panel);
33107 * Hides the tab for the specified panel.
33108 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33110 hidePanel : function(panel){
33111 if(this.tabs && (panel = this.getPanel(panel))){
33112 this.tabs.hideTab(panel.getEl().id);
33117 * Unhides the tab for a previously hidden panel.
33118 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33120 unhidePanel : function(panel){
33121 if(this.tabs && (panel = this.getPanel(panel))){
33122 this.tabs.unhideTab(panel.getEl().id);
33126 clearPanels : function(){
33127 while(this.panels.getCount() > 0){
33128 this.remove(this.panels.first());
33133 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33134 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33135 * @param {Boolean} preservePanel Overrides the config preservePanel option
33136 * @return {Roo.ContentPanel} The panel that was removed
33138 remove : function(panel, preservePanel)
33140 panel = this.getPanel(panel);
33145 this.fireEvent("beforeremove", this, panel, e);
33146 if(e.cancel === true){
33149 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33150 var panelId = panel.getId();
33151 this.panels.removeKey(panelId);
33153 document.body.appendChild(panel.getEl().dom);
33156 this.tabs.removeTab(panel.getEl().id);
33157 }else if (!preservePanel){
33158 this.bodyEl.dom.removeChild(panel.getEl().dom);
33160 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33161 var p = this.panels.first();
33162 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33163 tempEl.appendChild(p.getEl().dom);
33164 this.bodyEl.update("");
33165 this.bodyEl.dom.appendChild(p.getEl().dom);
33167 this.updateTitle(p.getTitle());
33169 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33170 this.setActivePanel(p);
33172 panel.setRegion(null);
33173 if(this.activePanel == panel){
33174 this.activePanel = null;
33176 if(this.config.autoDestroy !== false && preservePanel !== true){
33177 try{panel.destroy();}catch(e){}
33179 this.fireEvent("panelremoved", this, panel);
33184 * Returns the TabPanel component used by this region
33185 * @return {Roo.TabPanel}
33187 getTabs : function(){
33191 createTool : function(parentEl, className){
33192 var btn = Roo.DomHelper.append(parentEl, {
33194 cls: "x-layout-tools-button",
33197 cls: "roo-layout-tools-button-inner " + className,
33201 btn.addClassOnOver("roo-layout-tools-button-over");
33206 * Ext JS Library 1.1.1
33207 * Copyright(c) 2006-2007, Ext JS, LLC.
33209 * Originally Released Under LGPL - original licence link has changed is not relivant.
33212 * <script type="text/javascript">
33218 * @class Roo.SplitLayoutRegion
33219 * @extends Roo.LayoutRegion
33220 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33222 Roo.bootstrap.layout.Split = function(config){
33223 this.cursor = config.cursor;
33224 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33227 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33229 splitTip : "Drag to resize.",
33230 collapsibleSplitTip : "Drag to resize. Double click to hide.",
33231 useSplitTips : false,
33233 applyConfig : function(config){
33234 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33237 onRender : function(ctr,pos) {
33239 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33240 if(!this.config.split){
33245 var splitEl = Roo.DomHelper.append(ctr.dom, {
33247 id: this.el.id + "-split",
33248 cls: "roo-layout-split roo-layout-split-"+this.position,
33251 /** The SplitBar for this region
33252 * @type Roo.SplitBar */
33253 // does not exist yet...
33254 Roo.log([this.position, this.orientation]);
33256 this.split = new Roo.bootstrap.SplitBar({
33257 dragElement : splitEl,
33258 resizingElement: this.el,
33259 orientation : this.orientation
33262 this.split.on("moved", this.onSplitMove, this);
33263 this.split.useShim = this.config.useShim === true;
33264 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33265 if(this.useSplitTips){
33266 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33268 //if(config.collapsible){
33269 // this.split.el.on("dblclick", this.collapse, this);
33272 if(typeof this.config.minSize != "undefined"){
33273 this.split.minSize = this.config.minSize;
33275 if(typeof this.config.maxSize != "undefined"){
33276 this.split.maxSize = this.config.maxSize;
33278 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33279 this.hideSplitter();
33284 getHMaxSize : function(){
33285 var cmax = this.config.maxSize || 10000;
33286 var center = this.mgr.getRegion("center");
33287 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33290 getVMaxSize : function(){
33291 var cmax = this.config.maxSize || 10000;
33292 var center = this.mgr.getRegion("center");
33293 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33296 onSplitMove : function(split, newSize){
33297 this.fireEvent("resized", this, newSize);
33301 * Returns the {@link Roo.SplitBar} for this region.
33302 * @return {Roo.SplitBar}
33304 getSplitBar : function(){
33309 this.hideSplitter();
33310 Roo.bootstrap.layout.Split.superclass.hide.call(this);
33313 hideSplitter : function(){
33315 this.split.el.setLocation(-2000,-2000);
33316 this.split.el.hide();
33322 this.split.el.show();
33324 Roo.bootstrap.layout.Split.superclass.show.call(this);
33327 beforeSlide: function(){
33328 if(Roo.isGecko){// firefox overflow auto bug workaround
33329 this.bodyEl.clip();
33331 this.tabs.bodyEl.clip();
33333 if(this.activePanel){
33334 this.activePanel.getEl().clip();
33336 if(this.activePanel.beforeSlide){
33337 this.activePanel.beforeSlide();
33343 afterSlide : function(){
33344 if(Roo.isGecko){// firefox overflow auto bug workaround
33345 this.bodyEl.unclip();
33347 this.tabs.bodyEl.unclip();
33349 if(this.activePanel){
33350 this.activePanel.getEl().unclip();
33351 if(this.activePanel.afterSlide){
33352 this.activePanel.afterSlide();
33358 initAutoHide : function(){
33359 if(this.autoHide !== false){
33360 if(!this.autoHideHd){
33361 var st = new Roo.util.DelayedTask(this.slideIn, this);
33362 this.autoHideHd = {
33363 "mouseout": function(e){
33364 if(!e.within(this.el, true)){
33368 "mouseover" : function(e){
33374 this.el.on(this.autoHideHd);
33378 clearAutoHide : function(){
33379 if(this.autoHide !== false){
33380 this.el.un("mouseout", this.autoHideHd.mouseout);
33381 this.el.un("mouseover", this.autoHideHd.mouseover);
33385 clearMonitor : function(){
33386 Roo.get(document).un("click", this.slideInIf, this);
33389 // these names are backwards but not changed for compat
33390 slideOut : function(){
33391 if(this.isSlid || this.el.hasActiveFx()){
33394 this.isSlid = true;
33395 if(this.collapseBtn){
33396 this.collapseBtn.hide();
33398 this.closeBtnState = this.closeBtn.getStyle('display');
33399 this.closeBtn.hide();
33401 this.stickBtn.show();
33404 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33405 this.beforeSlide();
33406 this.el.setStyle("z-index", 10001);
33407 this.el.slideIn(this.getSlideAnchor(), {
33408 callback: function(){
33410 this.initAutoHide();
33411 Roo.get(document).on("click", this.slideInIf, this);
33412 this.fireEvent("slideshow", this);
33419 afterSlideIn : function(){
33420 this.clearAutoHide();
33421 this.isSlid = false;
33422 this.clearMonitor();
33423 this.el.setStyle("z-index", "");
33424 if(this.collapseBtn){
33425 this.collapseBtn.show();
33427 this.closeBtn.setStyle('display', this.closeBtnState);
33429 this.stickBtn.hide();
33431 this.fireEvent("slidehide", this);
33434 slideIn : function(cb){
33435 if(!this.isSlid || this.el.hasActiveFx()){
33439 this.isSlid = false;
33440 this.beforeSlide();
33441 this.el.slideOut(this.getSlideAnchor(), {
33442 callback: function(){
33443 this.el.setLeftTop(-10000, -10000);
33445 this.afterSlideIn();
33453 slideInIf : function(e){
33454 if(!e.within(this.el)){
33459 animateCollapse : function(){
33460 this.beforeSlide();
33461 this.el.setStyle("z-index", 20000);
33462 var anchor = this.getSlideAnchor();
33463 this.el.slideOut(anchor, {
33464 callback : function(){
33465 this.el.setStyle("z-index", "");
33466 this.collapsedEl.slideIn(anchor, {duration:.3});
33468 this.el.setLocation(-10000,-10000);
33470 this.fireEvent("collapsed", this);
33477 animateExpand : function(){
33478 this.beforeSlide();
33479 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33480 this.el.setStyle("z-index", 20000);
33481 this.collapsedEl.hide({
33484 this.el.slideIn(this.getSlideAnchor(), {
33485 callback : function(){
33486 this.el.setStyle("z-index", "");
33489 this.split.el.show();
33491 this.fireEvent("invalidated", this);
33492 this.fireEvent("expanded", this);
33520 getAnchor : function(){
33521 return this.anchors[this.position];
33524 getCollapseAnchor : function(){
33525 return this.canchors[this.position];
33528 getSlideAnchor : function(){
33529 return this.sanchors[this.position];
33532 getAlignAdj : function(){
33533 var cm = this.cmargins;
33534 switch(this.position){
33550 getExpandAdj : function(){
33551 var c = this.collapsedEl, cm = this.cmargins;
33552 switch(this.position){
33554 return [-(cm.right+c.getWidth()+cm.left), 0];
33557 return [cm.right+c.getWidth()+cm.left, 0];
33560 return [0, -(cm.top+cm.bottom+c.getHeight())];
33563 return [0, cm.top+cm.bottom+c.getHeight()];
33569 * Ext JS Library 1.1.1
33570 * Copyright(c) 2006-2007, Ext JS, LLC.
33572 * Originally Released Under LGPL - original licence link has changed is not relivant.
33575 * <script type="text/javascript">
33578 * These classes are private internal classes
33580 Roo.bootstrap.layout.Center = function(config){
33581 config.region = "center";
33582 Roo.bootstrap.layout.Region.call(this, config);
33583 this.visible = true;
33584 this.minWidth = config.minWidth || 20;
33585 this.minHeight = config.minHeight || 20;
33588 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33590 // center panel can't be hidden
33594 // center panel can't be hidden
33597 getMinWidth: function(){
33598 return this.minWidth;
33601 getMinHeight: function(){
33602 return this.minHeight;
33615 Roo.bootstrap.layout.North = function(config)
33617 config.region = 'north';
33618 config.cursor = 'n-resize';
33620 Roo.bootstrap.layout.Split.call(this, config);
33624 this.split.placement = Roo.bootstrap.SplitBar.TOP;
33625 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33626 this.split.el.addClass("roo-layout-split-v");
33628 var size = config.initialSize || config.height;
33629 if(typeof size != "undefined"){
33630 this.el.setHeight(size);
33633 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33635 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33639 getBox : function(){
33640 if(this.collapsed){
33641 return this.collapsedEl.getBox();
33643 var box = this.el.getBox();
33645 box.height += this.split.el.getHeight();
33650 updateBox : function(box){
33651 if(this.split && !this.collapsed){
33652 box.height -= this.split.el.getHeight();
33653 this.split.el.setLeft(box.x);
33654 this.split.el.setTop(box.y+box.height);
33655 this.split.el.setWidth(box.width);
33657 if(this.collapsed){
33658 this.updateBody(box.width, null);
33660 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33668 Roo.bootstrap.layout.South = function(config){
33669 config.region = 'south';
33670 config.cursor = 's-resize';
33671 Roo.bootstrap.layout.Split.call(this, config);
33673 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33674 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33675 this.split.el.addClass("roo-layout-split-v");
33677 var size = config.initialSize || config.height;
33678 if(typeof size != "undefined"){
33679 this.el.setHeight(size);
33683 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33684 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33685 getBox : function(){
33686 if(this.collapsed){
33687 return this.collapsedEl.getBox();
33689 var box = this.el.getBox();
33691 var sh = this.split.el.getHeight();
33698 updateBox : function(box){
33699 if(this.split && !this.collapsed){
33700 var sh = this.split.el.getHeight();
33703 this.split.el.setLeft(box.x);
33704 this.split.el.setTop(box.y-sh);
33705 this.split.el.setWidth(box.width);
33707 if(this.collapsed){
33708 this.updateBody(box.width, null);
33710 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33714 Roo.bootstrap.layout.East = function(config){
33715 config.region = "east";
33716 config.cursor = "e-resize";
33717 Roo.bootstrap.layout.Split.call(this, config);
33719 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33720 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33721 this.split.el.addClass("roo-layout-split-h");
33723 var size = config.initialSize || config.width;
33724 if(typeof size != "undefined"){
33725 this.el.setWidth(size);
33728 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33729 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33730 getBox : function(){
33731 if(this.collapsed){
33732 return this.collapsedEl.getBox();
33734 var box = this.el.getBox();
33736 var sw = this.split.el.getWidth();
33743 updateBox : function(box){
33744 if(this.split && !this.collapsed){
33745 var sw = this.split.el.getWidth();
33747 this.split.el.setLeft(box.x);
33748 this.split.el.setTop(box.y);
33749 this.split.el.setHeight(box.height);
33752 if(this.collapsed){
33753 this.updateBody(null, box.height);
33755 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33759 Roo.bootstrap.layout.West = function(config){
33760 config.region = "west";
33761 config.cursor = "w-resize";
33763 Roo.bootstrap.layout.Split.call(this, config);
33765 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33766 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33767 this.split.el.addClass("roo-layout-split-h");
33771 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33772 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33774 onRender: function(ctr, pos)
33776 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33777 var size = this.config.initialSize || this.config.width;
33778 if(typeof size != "undefined"){
33779 this.el.setWidth(size);
33783 getBox : function(){
33784 if(this.collapsed){
33785 return this.collapsedEl.getBox();
33787 var box = this.el.getBox();
33789 box.width += this.split.el.getWidth();
33794 updateBox : function(box){
33795 if(this.split && !this.collapsed){
33796 var sw = this.split.el.getWidth();
33798 this.split.el.setLeft(box.x+box.width);
33799 this.split.el.setTop(box.y);
33800 this.split.el.setHeight(box.height);
33802 if(this.collapsed){
33803 this.updateBody(null, box.height);
33805 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33808 Roo.namespace("Roo.bootstrap.panel");/*
33810 * Ext JS Library 1.1.1
33811 * Copyright(c) 2006-2007, Ext JS, LLC.
33813 * Originally Released Under LGPL - original licence link has changed is not relivant.
33816 * <script type="text/javascript">
33819 * @class Roo.ContentPanel
33820 * @extends Roo.util.Observable
33821 * A basic ContentPanel element.
33822 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
33823 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
33824 * @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
33825 * @cfg {Boolean} closable True if the panel can be closed/removed
33826 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
33827 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33828 * @cfg {Toolbar} toolbar A toolbar for this panel
33829 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
33830 * @cfg {String} title The title for this panel
33831 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33832 * @cfg {String} url Calls {@link #setUrl} with this value
33833 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33834 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
33835 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
33836 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
33839 * Create a new ContentPanel.
33840 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33841 * @param {String/Object} config A string to set only the title or a config object
33842 * @param {String} content (optional) Set the HTML content for this panel
33843 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33845 Roo.bootstrap.panel.Content = function( config){
33847 var el = config.el;
33848 var content = config.content;
33850 if(config.autoCreate){ // xtype is available if this is called from factory
33853 this.el = Roo.get(el);
33854 if(!this.el && config && config.autoCreate){
33855 if(typeof config.autoCreate == "object"){
33856 if(!config.autoCreate.id){
33857 config.autoCreate.id = config.id||el;
33859 this.el = Roo.DomHelper.append(document.body,
33860 config.autoCreate, true);
33862 var elcfg = { tag: "div",
33863 cls: "roo-layout-inactive-content",
33867 elcfg.html = config.html;
33871 this.el = Roo.DomHelper.append(document.body, elcfg , true);
33874 this.closable = false;
33875 this.loaded = false;
33876 this.active = false;
33879 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33881 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33883 this.wrapEl = this.el.wrap();
33885 if (config.toolbar.items) {
33886 ti = config.toolbar.items ;
33887 delete config.toolbar.items ;
33891 this.toolbar.render(this.wrapEl, 'before');
33892 for(var i =0;i < ti.length;i++) {
33893 // Roo.log(['add child', items[i]]);
33894 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33896 this.toolbar.items = nitems;
33897 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33898 delete config.toolbar;
33902 // xtype created footer. - not sure if will work as we normally have to render first..
33903 if (this.footer && !this.footer.el && this.footer.xtype) {
33904 if (!this.wrapEl) {
33905 this.wrapEl = this.el.wrap();
33908 this.footer.container = this.wrapEl.createChild();
33910 this.footer = Roo.factory(this.footer, Roo);
33915 if(typeof config == "string"){
33916 this.title = config;
33918 Roo.apply(this, config);
33922 this.resizeEl = Roo.get(this.resizeEl, true);
33924 this.resizeEl = this.el;
33926 // handle view.xtype
33934 * Fires when this panel is activated.
33935 * @param {Roo.ContentPanel} this
33939 * @event deactivate
33940 * Fires when this panel is activated.
33941 * @param {Roo.ContentPanel} this
33943 "deactivate" : true,
33947 * Fires when this panel is resized if fitToFrame is true.
33948 * @param {Roo.ContentPanel} this
33949 * @param {Number} width The width after any component adjustments
33950 * @param {Number} height The height after any component adjustments
33956 * Fires when this tab is created
33957 * @param {Roo.ContentPanel} this
33968 if(this.autoScroll){
33969 this.resizeEl.setStyle("overflow", "auto");
33971 // fix randome scrolling
33972 //this.el.on('scroll', function() {
33973 // Roo.log('fix random scolling');
33974 // this.scrollTo('top',0);
33977 content = content || this.content;
33979 this.setContent(content);
33981 if(config && config.url){
33982 this.setUrl(this.url, this.params, this.loadOnce);
33987 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33989 if (this.view && typeof(this.view.xtype) != 'undefined') {
33990 this.view.el = this.el.appendChild(document.createElement("div"));
33991 this.view = Roo.factory(this.view);
33992 this.view.render && this.view.render(false, '');
33996 this.fireEvent('render', this);
33999 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34001 setRegion : function(region){
34002 this.region = region;
34004 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
34006 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
34011 * Returns the toolbar for this Panel if one was configured.
34012 * @return {Roo.Toolbar}
34014 getToolbar : function(){
34015 return this.toolbar;
34018 setActiveState : function(active){
34019 this.active = active;
34021 this.fireEvent("deactivate", this);
34023 this.fireEvent("activate", this);
34027 * Updates this panel's element
34028 * @param {String} content The new content
34029 * @param {Boolean} loadScripts (optional) true to look for and process scripts
34031 setContent : function(content, loadScripts){
34032 this.el.update(content, loadScripts);
34035 ignoreResize : function(w, h){
34036 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34039 this.lastSize = {width: w, height: h};
34044 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34045 * @return {Roo.UpdateManager} The UpdateManager
34047 getUpdateManager : function(){
34048 return this.el.getUpdateManager();
34051 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34052 * @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:
34055 url: "your-url.php",
34056 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34057 callback: yourFunction,
34058 scope: yourObject, //(optional scope)
34061 text: "Loading...",
34066 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34067 * 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.
34068 * @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}
34069 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34070 * @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.
34071 * @return {Roo.ContentPanel} this
34074 var um = this.el.getUpdateManager();
34075 um.update.apply(um, arguments);
34081 * 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.
34082 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34083 * @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)
34084 * @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)
34085 * @return {Roo.UpdateManager} The UpdateManager
34087 setUrl : function(url, params, loadOnce){
34088 if(this.refreshDelegate){
34089 this.removeListener("activate", this.refreshDelegate);
34091 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34092 this.on("activate", this.refreshDelegate);
34093 return this.el.getUpdateManager();
34096 _handleRefresh : function(url, params, loadOnce){
34097 if(!loadOnce || !this.loaded){
34098 var updater = this.el.getUpdateManager();
34099 updater.update(url, params, this._setLoaded.createDelegate(this));
34103 _setLoaded : function(){
34104 this.loaded = true;
34108 * Returns this panel's id
34111 getId : function(){
34116 * Returns this panel's element - used by regiosn to add.
34117 * @return {Roo.Element}
34119 getEl : function(){
34120 return this.wrapEl || this.el;
34125 adjustForComponents : function(width, height)
34127 //Roo.log('adjustForComponents ');
34128 if(this.resizeEl != this.el){
34129 width -= this.el.getFrameWidth('lr');
34130 height -= this.el.getFrameWidth('tb');
34133 var te = this.toolbar.getEl();
34134 height -= te.getHeight();
34135 te.setWidth(width);
34138 var te = this.footer.getEl();
34139 Roo.log("footer:" + te.getHeight());
34141 height -= te.getHeight();
34142 te.setWidth(width);
34146 if(this.adjustments){
34147 width += this.adjustments[0];
34148 height += this.adjustments[1];
34150 return {"width": width, "height": height};
34153 setSize : function(width, height){
34154 if(this.fitToFrame && !this.ignoreResize(width, height)){
34155 if(this.fitContainer && this.resizeEl != this.el){
34156 this.el.setSize(width, height);
34158 var size = this.adjustForComponents(width, height);
34159 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34160 this.fireEvent('resize', this, size.width, size.height);
34165 * Returns this panel's title
34168 getTitle : function(){
34173 * Set this panel's title
34174 * @param {String} title
34176 setTitle : function(title){
34177 this.title = title;
34179 this.region.updatePanelTitle(this, title);
34184 * Returns true is this panel was configured to be closable
34185 * @return {Boolean}
34187 isClosable : function(){
34188 return this.closable;
34191 beforeSlide : function(){
34193 this.resizeEl.clip();
34196 afterSlide : function(){
34198 this.resizeEl.unclip();
34202 * Force a content refresh from the URL specified in the {@link #setUrl} method.
34203 * Will fail silently if the {@link #setUrl} method has not been called.
34204 * This does not activate the panel, just updates its content.
34206 refresh : function(){
34207 if(this.refreshDelegate){
34208 this.loaded = false;
34209 this.refreshDelegate();
34214 * Destroys this panel
34216 destroy : function(){
34217 this.el.removeAllListeners();
34218 var tempEl = document.createElement("span");
34219 tempEl.appendChild(this.el.dom);
34220 tempEl.innerHTML = "";
34226 * form - if the content panel contains a form - this is a reference to it.
34227 * @type {Roo.form.Form}
34231 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34232 * This contains a reference to it.
34238 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34248 * @param {Object} cfg Xtype definition of item to add.
34252 getChildContainer: function () {
34253 return this.getEl();
34258 var ret = new Roo.factory(cfg);
34263 if (cfg.xtype.match(/^Form$/)) {
34266 //if (this.footer) {
34267 // el = this.footer.container.insertSibling(false, 'before');
34269 el = this.el.createChild();
34272 this.form = new Roo.form.Form(cfg);
34275 if ( this.form.allItems.length) {
34276 this.form.render(el.dom);
34280 // should only have one of theses..
34281 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34282 // views.. should not be just added - used named prop 'view''
34284 cfg.el = this.el.appendChild(document.createElement("div"));
34287 var ret = new Roo.factory(cfg);
34289 ret.render && ret.render(false, ''); // render blank..
34299 * @class Roo.bootstrap.panel.Grid
34300 * @extends Roo.bootstrap.panel.Content
34302 * Create a new GridPanel.
34303 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34304 * @param {Object} config A the config object
34310 Roo.bootstrap.panel.Grid = function(config)
34314 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34315 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34317 config.el = this.wrapper;
34318 //this.el = this.wrapper;
34320 if (config.container) {
34321 // ctor'ed from a Border/panel.grid
34324 this.wrapper.setStyle("overflow", "hidden");
34325 this.wrapper.addClass('roo-grid-container');
34330 if(config.toolbar){
34331 var tool_el = this.wrapper.createChild();
34332 this.toolbar = Roo.factory(config.toolbar);
34334 if (config.toolbar.items) {
34335 ti = config.toolbar.items ;
34336 delete config.toolbar.items ;
34340 this.toolbar.render(tool_el);
34341 for(var i =0;i < ti.length;i++) {
34342 // Roo.log(['add child', items[i]]);
34343 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34345 this.toolbar.items = nitems;
34347 delete config.toolbar;
34350 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34351 config.grid.scrollBody = true;;
34352 config.grid.monitorWindowResize = false; // turn off autosizing
34353 config.grid.autoHeight = false;
34354 config.grid.autoWidth = false;
34356 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34358 if (config.background) {
34359 // render grid on panel activation (if panel background)
34360 this.on('activate', function(gp) {
34361 if (!gp.grid.rendered) {
34362 gp.grid.render(el);
34363 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34369 this.grid.render(this.wrapper);
34370 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34373 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34374 // ??? needed ??? config.el = this.wrapper;
34379 // xtype created footer. - not sure if will work as we normally have to render first..
34380 if (this.footer && !this.footer.el && this.footer.xtype) {
34382 var ctr = this.grid.getView().getFooterPanel(true);
34383 this.footer.dataSource = this.grid.dataSource;
34384 this.footer = Roo.factory(this.footer, Roo);
34385 this.footer.render(ctr);
34395 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34396 getId : function(){
34397 return this.grid.id;
34401 * Returns the grid for this panel
34402 * @return {Roo.bootstrap.Table}
34404 getGrid : function(){
34408 setSize : function(width, height){
34409 if(!this.ignoreResize(width, height)){
34410 var grid = this.grid;
34411 var size = this.adjustForComponents(width, height);
34412 var gridel = grid.getGridEl();
34413 gridel.setSize(size.width, size.height);
34415 var thd = grid.getGridEl().select('thead',true).first();
34416 var tbd = grid.getGridEl().select('tbody', true).first();
34418 tbd.setSize(width, height - thd.getHeight());
34427 beforeSlide : function(){
34428 this.grid.getView().scroller.clip();
34431 afterSlide : function(){
34432 this.grid.getView().scroller.unclip();
34435 destroy : function(){
34436 this.grid.destroy();
34438 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
34443 * @class Roo.bootstrap.panel.Nest
34444 * @extends Roo.bootstrap.panel.Content
34446 * Create a new Panel, that can contain a layout.Border.
34449 * @param {Roo.BorderLayout} layout The layout for this panel
34450 * @param {String/Object} config A string to set only the title or a config object
34452 Roo.bootstrap.panel.Nest = function(config)
34454 // construct with only one argument..
34455 /* FIXME - implement nicer consturctors
34456 if (layout.layout) {
34458 layout = config.layout;
34459 delete config.layout;
34461 if (layout.xtype && !layout.getEl) {
34462 // then layout needs constructing..
34463 layout = Roo.factory(layout, Roo);
34467 config.el = config.layout.getEl();
34469 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34471 config.layout.monitorWindowResize = false; // turn off autosizing
34472 this.layout = config.layout;
34473 this.layout.getEl().addClass("roo-layout-nested-layout");
34480 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34482 setSize : function(width, height){
34483 if(!this.ignoreResize(width, height)){
34484 var size = this.adjustForComponents(width, height);
34485 var el = this.layout.getEl();
34486 el.setSize(size.width, size.height);
34487 var touch = el.dom.offsetWidth;
34488 this.layout.layout();
34489 // ie requires a double layout on the first pass
34490 if(Roo.isIE && !this.initialized){
34491 this.initialized = true;
34492 this.layout.layout();
34497 // activate all subpanels if not currently active..
34499 setActiveState : function(active){
34500 this.active = active;
34502 this.fireEvent("deactivate", this);
34506 this.fireEvent("activate", this);
34507 // not sure if this should happen before or after..
34508 if (!this.layout) {
34509 return; // should not happen..
34512 for (var r in this.layout.regions) {
34513 reg = this.layout.getRegion(r);
34514 if (reg.getActivePanel()) {
34515 //reg.showPanel(reg.getActivePanel()); // force it to activate..
34516 reg.setActivePanel(reg.getActivePanel());
34519 if (!reg.panels.length) {
34522 reg.showPanel(reg.getPanel(0));
34531 * Returns the nested BorderLayout for this panel
34532 * @return {Roo.BorderLayout}
34534 getLayout : function(){
34535 return this.layout;
34539 * Adds a xtype elements to the layout of the nested panel
34543 xtype : 'ContentPanel',
34550 xtype : 'NestedLayoutPanel',
34556 items : [ ... list of content panels or nested layout panels.. ]
34560 * @param {Object} cfg Xtype definition of item to add.
34562 addxtype : function(cfg) {
34563 return this.layout.addxtype(cfg);
34568 * Ext JS Library 1.1.1
34569 * Copyright(c) 2006-2007, Ext JS, LLC.
34571 * Originally Released Under LGPL - original licence link has changed is not relivant.
34574 * <script type="text/javascript">
34577 * @class Roo.TabPanel
34578 * @extends Roo.util.Observable
34579 * A lightweight tab container.
34583 // basic tabs 1, built from existing content
34584 var tabs = new Roo.TabPanel("tabs1");
34585 tabs.addTab("script", "View Script");
34586 tabs.addTab("markup", "View Markup");
34587 tabs.activate("script");
34589 // more advanced tabs, built from javascript
34590 var jtabs = new Roo.TabPanel("jtabs");
34591 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34593 // set up the UpdateManager
34594 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34595 var updater = tab2.getUpdateManager();
34596 updater.setDefaultUrl("ajax1.htm");
34597 tab2.on('activate', updater.refresh, updater, true);
34599 // Use setUrl for Ajax loading
34600 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34601 tab3.setUrl("ajax2.htm", null, true);
34604 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34607 jtabs.activate("jtabs-1");
34610 * Create a new TabPanel.
34611 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34612 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34614 Roo.bootstrap.panel.Tabs = function(config){
34616 * The container element for this TabPanel.
34617 * @type Roo.Element
34619 this.el = Roo.get(config.el);
34622 if(typeof config == "boolean"){
34623 this.tabPosition = config ? "bottom" : "top";
34625 Roo.apply(this, config);
34629 if(this.tabPosition == "bottom"){
34630 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34631 this.el.addClass("roo-tabs-bottom");
34633 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34634 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34635 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34637 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34639 if(this.tabPosition != "bottom"){
34640 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34641 * @type Roo.Element
34643 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34644 this.el.addClass("roo-tabs-top");
34648 this.bodyEl.setStyle("position", "relative");
34650 this.active = null;
34651 this.activateDelegate = this.activate.createDelegate(this);
34656 * Fires when the active tab changes
34657 * @param {Roo.TabPanel} this
34658 * @param {Roo.TabPanelItem} activePanel The new active tab
34662 * @event beforetabchange
34663 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34664 * @param {Roo.TabPanel} this
34665 * @param {Object} e Set cancel to true on this object to cancel the tab change
34666 * @param {Roo.TabPanelItem} tab The tab being changed to
34668 "beforetabchange" : true
34671 Roo.EventManager.onWindowResize(this.onResize, this);
34672 this.cpad = this.el.getPadding("lr");
34673 this.hiddenCount = 0;
34676 // toolbar on the tabbar support...
34677 if (this.toolbar) {
34678 alert("no toolbar support yet");
34679 this.toolbar = false;
34681 var tcfg = this.toolbar;
34682 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
34683 this.toolbar = new Roo.Toolbar(tcfg);
34684 if (Roo.isSafari) {
34685 var tbl = tcfg.container.child('table', true);
34686 tbl.setAttribute('width', '100%');
34694 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34697 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34699 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34701 tabPosition : "top",
34703 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34705 currentTabWidth : 0,
34707 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34711 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34715 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34717 preferredTabWidth : 175,
34719 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34721 resizeTabs : false,
34723 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34725 monitorResize : true,
34727 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
34732 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34733 * @param {String} id The id of the div to use <b>or create</b>
34734 * @param {String} text The text for the tab
34735 * @param {String} content (optional) Content to put in the TabPanelItem body
34736 * @param {Boolean} closable (optional) True to create a close icon on the tab
34737 * @return {Roo.TabPanelItem} The created TabPanelItem
34739 addTab : function(id, text, content, closable)
34741 var item = new Roo.bootstrap.panel.TabItem({
34745 closable : closable
34747 this.addTabItem(item);
34749 item.setContent(content);
34755 * Returns the {@link Roo.TabPanelItem} with the specified id/index
34756 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34757 * @return {Roo.TabPanelItem}
34759 getTab : function(id){
34760 return this.items[id];
34764 * Hides the {@link Roo.TabPanelItem} with the specified id/index
34765 * @param {String/Number} id The id or index of the TabPanelItem to hide.
34767 hideTab : function(id){
34768 var t = this.items[id];
34771 this.hiddenCount++;
34772 this.autoSizeTabs();
34777 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34778 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34780 unhideTab : function(id){
34781 var t = this.items[id];
34783 t.setHidden(false);
34784 this.hiddenCount--;
34785 this.autoSizeTabs();
34790 * Adds an existing {@link Roo.TabPanelItem}.
34791 * @param {Roo.TabPanelItem} item The TabPanelItem to add
34793 addTabItem : function(item){
34794 this.items[item.id] = item;
34795 this.items.push(item);
34796 // if(this.resizeTabs){
34797 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34798 // this.autoSizeTabs();
34800 // item.autoSize();
34805 * Removes a {@link Roo.TabPanelItem}.
34806 * @param {String/Number} id The id or index of the TabPanelItem to remove.
34808 removeTab : function(id){
34809 var items = this.items;
34810 var tab = items[id];
34811 if(!tab) { return; }
34812 var index = items.indexOf(tab);
34813 if(this.active == tab && items.length > 1){
34814 var newTab = this.getNextAvailable(index);
34819 this.stripEl.dom.removeChild(tab.pnode.dom);
34820 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34821 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34823 items.splice(index, 1);
34824 delete this.items[tab.id];
34825 tab.fireEvent("close", tab);
34826 tab.purgeListeners();
34827 this.autoSizeTabs();
34830 getNextAvailable : function(start){
34831 var items = this.items;
34833 // look for a next tab that will slide over to
34834 // replace the one being removed
34835 while(index < items.length){
34836 var item = items[++index];
34837 if(item && !item.isHidden()){
34841 // if one isn't found select the previous tab (on the left)
34844 var item = items[--index];
34845 if(item && !item.isHidden()){
34853 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34854 * @param {String/Number} id The id or index of the TabPanelItem to disable.
34856 disableTab : function(id){
34857 var tab = this.items[id];
34858 if(tab && this.active != tab){
34864 * Enables a {@link Roo.TabPanelItem} that is disabled.
34865 * @param {String/Number} id The id or index of the TabPanelItem to enable.
34867 enableTab : function(id){
34868 var tab = this.items[id];
34873 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34874 * @param {String/Number} id The id or index of the TabPanelItem to activate.
34875 * @return {Roo.TabPanelItem} The TabPanelItem.
34877 activate : function(id){
34878 var tab = this.items[id];
34882 if(tab == this.active || tab.disabled){
34886 this.fireEvent("beforetabchange", this, e, tab);
34887 if(e.cancel !== true && !tab.disabled){
34889 this.active.hide();
34891 this.active = this.items[id];
34892 this.active.show();
34893 this.fireEvent("tabchange", this, this.active);
34899 * Gets the active {@link Roo.TabPanelItem}.
34900 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34902 getActiveTab : function(){
34903 return this.active;
34907 * Updates the tab body element to fit the height of the container element
34908 * for overflow scrolling
34909 * @param {Number} targetHeight (optional) Override the starting height from the elements height
34911 syncHeight : function(targetHeight){
34912 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34913 var bm = this.bodyEl.getMargins();
34914 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34915 this.bodyEl.setHeight(newHeight);
34919 onResize : function(){
34920 if(this.monitorResize){
34921 this.autoSizeTabs();
34926 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34928 beginUpdate : function(){
34929 this.updating = true;
34933 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34935 endUpdate : function(){
34936 this.updating = false;
34937 this.autoSizeTabs();
34941 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34943 autoSizeTabs : function(){
34944 var count = this.items.length;
34945 var vcount = count - this.hiddenCount;
34946 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34949 var w = Math.max(this.el.getWidth() - this.cpad, 10);
34950 var availWidth = Math.floor(w / vcount);
34951 var b = this.stripBody;
34952 if(b.getWidth() > w){
34953 var tabs = this.items;
34954 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34955 if(availWidth < this.minTabWidth){
34956 /*if(!this.sleft){ // incomplete scrolling code
34957 this.createScrollButtons();
34960 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34963 if(this.currentTabWidth < this.preferredTabWidth){
34964 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34970 * Returns the number of tabs in this TabPanel.
34973 getCount : function(){
34974 return this.items.length;
34978 * Resizes all the tabs to the passed width
34979 * @param {Number} The new width
34981 setTabWidth : function(width){
34982 this.currentTabWidth = width;
34983 for(var i = 0, len = this.items.length; i < len; i++) {
34984 if(!this.items[i].isHidden()) {
34985 this.items[i].setWidth(width);
34991 * Destroys this TabPanel
34992 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34994 destroy : function(removeEl){
34995 Roo.EventManager.removeResizeListener(this.onResize, this);
34996 for(var i = 0, len = this.items.length; i < len; i++){
34997 this.items[i].purgeListeners();
34999 if(removeEl === true){
35000 this.el.update("");
35005 createStrip : function(container)
35007 var strip = document.createElement("nav");
35008 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
35009 container.appendChild(strip);
35013 createStripList : function(strip)
35015 // div wrapper for retard IE
35016 // returns the "tr" element.
35017 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
35018 //'<div class="x-tabs-strip-wrap">'+
35019 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
35020 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
35021 return strip.firstChild; //.firstChild.firstChild.firstChild;
35023 createBody : function(container)
35025 var body = document.createElement("div");
35026 Roo.id(body, "tab-body");
35027 //Roo.fly(body).addClass("x-tabs-body");
35028 Roo.fly(body).addClass("tab-content");
35029 container.appendChild(body);
35032 createItemBody :function(bodyEl, id){
35033 var body = Roo.getDom(id);
35035 body = document.createElement("div");
35038 //Roo.fly(body).addClass("x-tabs-item-body");
35039 Roo.fly(body).addClass("tab-pane");
35040 bodyEl.insertBefore(body, bodyEl.firstChild);
35044 createStripElements : function(stripEl, text, closable)
35046 var td = document.createElement("li"); // was td..
35049 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
35052 stripEl.appendChild(td);
35054 td.className = "x-tabs-closable";
35055 if(!this.closeTpl){
35056 this.closeTpl = new Roo.Template(
35057 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35058 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
35059 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
35062 var el = this.closeTpl.overwrite(td, {"text": text});
35063 var close = el.getElementsByTagName("div")[0];
35064 var inner = el.getElementsByTagName("em")[0];
35065 return {"el": el, "close": close, "inner": inner};
35068 // not sure what this is..
35070 //this.tabTpl = new Roo.Template(
35071 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35072 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
35074 this.tabTpl = new Roo.Template(
35076 '<span unselectable="on"' +
35077 (this.disableTooltips ? '' : ' title="{text}"') +
35078 ' >{text}</span></span></a>'
35082 var el = this.tabTpl.overwrite(td, {"text": text});
35083 var inner = el.getElementsByTagName("span")[0];
35084 return {"el": el, "inner": inner};
35092 * @class Roo.TabPanelItem
35093 * @extends Roo.util.Observable
35094 * Represents an individual item (tab plus body) in a TabPanel.
35095 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35096 * @param {String} id The id of this TabPanelItem
35097 * @param {String} text The text for the tab of this TabPanelItem
35098 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35100 Roo.bootstrap.panel.TabItem = function(config){
35102 * The {@link Roo.TabPanel} this TabPanelItem belongs to
35103 * @type Roo.TabPanel
35105 this.tabPanel = config.panel;
35107 * The id for this TabPanelItem
35110 this.id = config.id;
35112 this.disabled = false;
35114 this.text = config.text;
35116 this.loaded = false;
35117 this.closable = config.closable;
35120 * The body element for this TabPanelItem.
35121 * @type Roo.Element
35123 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35124 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35125 this.bodyEl.setStyle("display", "block");
35126 this.bodyEl.setStyle("zoom", "1");
35127 //this.hideAction();
35129 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
35131 this.el = Roo.get(els.el);
35132 this.inner = Roo.get(els.inner, true);
35133 this.textEl = Roo.get(this.el.dom.firstChild, true);
35134 this.pnode = Roo.get(els.el.parentNode, true);
35135 this.el.on("mousedown", this.onTabMouseDown, this);
35136 this.el.on("click", this.onTabClick, this);
35138 if(config.closable){
35139 var c = Roo.get(els.close, true);
35140 c.dom.title = this.closeText;
35141 c.addClassOnOver("close-over");
35142 c.on("click", this.closeClick, this);
35148 * Fires when this tab becomes the active tab.
35149 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35150 * @param {Roo.TabPanelItem} this
35154 * @event beforeclose
35155 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35156 * @param {Roo.TabPanelItem} this
35157 * @param {Object} e Set cancel to true on this object to cancel the close.
35159 "beforeclose": true,
35162 * Fires when this tab is closed.
35163 * @param {Roo.TabPanelItem} this
35167 * @event deactivate
35168 * Fires when this tab is no longer the active tab.
35169 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35170 * @param {Roo.TabPanelItem} this
35172 "deactivate" : true
35174 this.hidden = false;
35176 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35179 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35181 purgeListeners : function(){
35182 Roo.util.Observable.prototype.purgeListeners.call(this);
35183 this.el.removeAllListeners();
35186 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35189 this.pnode.addClass("active");
35192 this.tabPanel.stripWrap.repaint();
35194 this.fireEvent("activate", this.tabPanel, this);
35198 * Returns true if this tab is the active tab.
35199 * @return {Boolean}
35201 isActive : function(){
35202 return this.tabPanel.getActiveTab() == this;
35206 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35209 this.pnode.removeClass("active");
35211 this.fireEvent("deactivate", this.tabPanel, this);
35214 hideAction : function(){
35215 this.bodyEl.hide();
35216 this.bodyEl.setStyle("position", "absolute");
35217 this.bodyEl.setLeft("-20000px");
35218 this.bodyEl.setTop("-20000px");
35221 showAction : function(){
35222 this.bodyEl.setStyle("position", "relative");
35223 this.bodyEl.setTop("");
35224 this.bodyEl.setLeft("");
35225 this.bodyEl.show();
35229 * Set the tooltip for the tab.
35230 * @param {String} tooltip The tab's tooltip
35232 setTooltip : function(text){
35233 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35234 this.textEl.dom.qtip = text;
35235 this.textEl.dom.removeAttribute('title');
35237 this.textEl.dom.title = text;
35241 onTabClick : function(e){
35242 e.preventDefault();
35243 this.tabPanel.activate(this.id);
35246 onTabMouseDown : function(e){
35247 e.preventDefault();
35248 this.tabPanel.activate(this.id);
35251 getWidth : function(){
35252 return this.inner.getWidth();
35255 setWidth : function(width){
35256 var iwidth = width - this.pnode.getPadding("lr");
35257 this.inner.setWidth(iwidth);
35258 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35259 this.pnode.setWidth(width);
35263 * Show or hide the tab
35264 * @param {Boolean} hidden True to hide or false to show.
35266 setHidden : function(hidden){
35267 this.hidden = hidden;
35268 this.pnode.setStyle("display", hidden ? "none" : "");
35272 * Returns true if this tab is "hidden"
35273 * @return {Boolean}
35275 isHidden : function(){
35276 return this.hidden;
35280 * Returns the text for this tab
35283 getText : function(){
35287 autoSize : function(){
35288 //this.el.beginMeasure();
35289 this.textEl.setWidth(1);
35291 * #2804 [new] Tabs in Roojs
35292 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35294 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35295 //this.el.endMeasure();
35299 * Sets the text for the tab (Note: this also sets the tooltip text)
35300 * @param {String} text The tab's text and tooltip
35302 setText : function(text){
35304 this.textEl.update(text);
35305 this.setTooltip(text);
35306 //if(!this.tabPanel.resizeTabs){
35307 // this.autoSize();
35311 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35313 activate : function(){
35314 this.tabPanel.activate(this.id);
35318 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35320 disable : function(){
35321 if(this.tabPanel.active != this){
35322 this.disabled = true;
35323 this.pnode.addClass("disabled");
35328 * Enables this TabPanelItem if it was previously disabled.
35330 enable : function(){
35331 this.disabled = false;
35332 this.pnode.removeClass("disabled");
35336 * Sets the content for this TabPanelItem.
35337 * @param {String} content The content
35338 * @param {Boolean} loadScripts true to look for and load scripts
35340 setContent : function(content, loadScripts){
35341 this.bodyEl.update(content, loadScripts);
35345 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35346 * @return {Roo.UpdateManager} The UpdateManager
35348 getUpdateManager : function(){
35349 return this.bodyEl.getUpdateManager();
35353 * Set a URL to be used to load the content for this TabPanelItem.
35354 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35355 * @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)
35356 * @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)
35357 * @return {Roo.UpdateManager} The UpdateManager
35359 setUrl : function(url, params, loadOnce){
35360 if(this.refreshDelegate){
35361 this.un('activate', this.refreshDelegate);
35363 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35364 this.on("activate", this.refreshDelegate);
35365 return this.bodyEl.getUpdateManager();
35369 _handleRefresh : function(url, params, loadOnce){
35370 if(!loadOnce || !this.loaded){
35371 var updater = this.bodyEl.getUpdateManager();
35372 updater.update(url, params, this._setLoaded.createDelegate(this));
35377 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
35378 * Will fail silently if the setUrl method has not been called.
35379 * This does not activate the panel, just updates its content.
35381 refresh : function(){
35382 if(this.refreshDelegate){
35383 this.loaded = false;
35384 this.refreshDelegate();
35389 _setLoaded : function(){
35390 this.loaded = true;
35394 closeClick : function(e){
35397 this.fireEvent("beforeclose", this, o);
35398 if(o.cancel !== true){
35399 this.tabPanel.removeTab(this.id);
35403 * The text displayed in the tooltip for the close icon.
35406 closeText : "Close this tab"