4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
105 cfg.id = this.id || Roo.id();
107 // fill in the extra attributes
108 if (this.xattr && typeof(this.xattr) =='object') {
109 for (var i in this.xattr) {
110 cfg[i] = this.xattr[i];
115 cfg.dataId = this.dataId;
119 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
122 if (this.style) { // fixme needs to support more complex style data.
123 cfg.style = this.style;
127 cfg.name = this.name;
130 this.el = ct.createChild(cfg, position);
133 this.tooltipEl().attr('tooltip', this.tooltip);
136 if(this.tabIndex !== undefined){
137 this.el.dom.setAttribute('tabIndex', this.tabIndex);
144 * Fetch the element to add children to
145 * @return {Roo.Element} defaults to this.el
147 getChildContainer : function()
152 * Fetch the element to display the tooltip on.
153 * @return {Roo.Element} defaults to this.el
155 tooltipEl : function()
160 addxtype : function(tree,cntr)
164 cn = Roo.factory(tree);
165 //Roo.log(['addxtype', cn]);
167 cn.parentType = this.xtype; //??
168 cn.parentId = this.id;
170 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171 if (typeof(cn.container_method) == 'string') {
172 cntr = cn.container_method;
176 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
178 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
180 var build_from_html = Roo.XComponent.build_from_html;
182 var is_body = (tree.xtype == 'Body') ;
184 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186 var self_cntr_el = Roo.get(this[cntr](false));
188 // do not try and build conditional elements
189 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
193 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195 return this.addxtypeChild(tree,cntr, is_body);
198 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
201 return this.addxtypeChild(Roo.apply({}, tree),cntr);
204 Roo.log('skipping render');
210 if (!build_from_html) {
214 // this i think handles overlaying multiple children of the same type
215 // with the sam eelement.. - which might be buggy..
217 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
223 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
227 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
234 addxtypeChild : function (tree, cntr, is_body)
236 Roo.debug && Roo.log('addxtypeChild:' + cntr);
238 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
241 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242 (typeof(tree['flexy:foreach']) != 'undefined');
246 skip_children = false;
247 // render the element if it's not BODY.
250 cn = Roo.factory(tree);
252 cn.parentType = this.xtype; //??
253 cn.parentId = this.id;
255 var build_from_html = Roo.XComponent.build_from_html;
258 // does the container contain child eleemnts with 'xtype' attributes.
259 // that match this xtype..
260 // note - when we render we create these as well..
261 // so we should check to see if body has xtype set.
262 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
264 var self_cntr_el = Roo.get(this[cntr](false));
265 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
267 //Roo.log(Roo.XComponent.build_from_html);
268 //Roo.log("got echild:");
271 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272 // and are not displayed -this causes this to use up the wrong element when matching.
273 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
276 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
283 //echild.dom.removeAttribute('xtype');
285 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286 Roo.debug && Roo.log(self_cntr_el);
287 Roo.debug && Roo.log(echild);
288 Roo.debug && Roo.log(cn);
294 // if object has flexy:if - then it may or may not be rendered.
295 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
296 // skip a flexy if element.
297 Roo.debug && Roo.log('skipping render');
298 Roo.debug && Roo.log(tree);
300 Roo.debug && Roo.log('skipping all children');
301 skip_children = true;
306 // actually if flexy:foreach is found, we really want to create
307 // multiple copies here...
309 //Roo.log(this[cntr]());
310 // some elements do not have render methods.. like the layouts...
311 cn.render && cn.render(this[cntr](true));
313 // then add the element..
321 if (typeof (tree.menu) != 'undefined') {
322 tree.menu.parentType = cn.xtype;
323 tree.menu.triggerEl = cn.el;
324 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
328 if (!tree.items || !tree.items.length) {
330 //Roo.log(["no children", this]);
335 var items = tree.items;
338 //Roo.log(items.length);
340 if (!skip_children) {
341 for(var i =0;i < items.length;i++) {
342 // Roo.log(['add child', items[i]]);
343 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
349 //Roo.log("fire childrenrendered");
351 cn.fireEvent('childrenrendered', this);
356 * Show a component - removes 'hidden' class
361 this.el.removeClass('hidden');
365 * Hide a component - adds 'hidden' class
369 if (this.el && !this.el.hasClass('hidden')) {
370 this.el.addClass('hidden');
384 * @class Roo.bootstrap.Body
385 * @extends Roo.bootstrap.Component
386 * Bootstrap Body class
390 * @param {Object} config The config object
393 Roo.bootstrap.Body = function(config){
394 Roo.bootstrap.Body.superclass.constructor.call(this, config);
395 this.el = Roo.get(document.body);
396 if (this.cls && this.cls.length) {
397 Roo.get(document.body).addClass(this.cls);
401 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
403 is_body : true,// just to make sure it's constructed?
408 onRender : function(ct, position)
410 /* Roo.log("Roo.bootstrap.Body - onRender");
411 if (this.cls && this.cls.length) {
412 Roo.get(document.body).addClass(this.cls);
432 * @class Roo.bootstrap.ButtonGroup
433 * @extends Roo.bootstrap.Component
434 * Bootstrap ButtonGroup class
435 * @cfg {String} size lg | sm | xs (default empty normal)
436 * @cfg {String} align vertical | justified (default none)
437 * @cfg {String} direction up | down (default down)
438 * @cfg {Boolean} toolbar false | true
439 * @cfg {Boolean} btn true | false
444 * @param {Object} config The config object
447 Roo.bootstrap.ButtonGroup = function(config){
448 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
459 getAutoCreate : function(){
465 cfg.html = this.html || cfg.html;
476 if (['vertical','justified'].indexOf(this.align)!==-1) {
477 cfg.cls = 'btn-group-' + this.align;
479 if (this.align == 'justified') {
480 console.log(this.items);
484 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
485 cfg.cls += ' btn-group-' + this.size;
488 if (this.direction == 'up') {
489 cfg.cls += ' dropup' ;
505 * @class Roo.bootstrap.Button
506 * @extends Roo.bootstrap.Component
507 * Bootstrap Button class
508 * @cfg {String} html The button content
509 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
510 * @cfg {String} size ( lg | sm | xs)
511 * @cfg {String} tag ( a | input | submit)
512 * @cfg {String} href empty or href
513 * @cfg {Boolean} disabled default false;
514 * @cfg {Boolean} isClose default false;
515 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
516 * @cfg {String} badge text for badge
517 * @cfg {String} theme default
518 * @cfg {Boolean} inverse
519 * @cfg {Boolean} toggle
520 * @cfg {String} ontext text for on toggle state
521 * @cfg {String} offtext text for off toggle state
522 * @cfg {Boolean} defaulton
523 * @cfg {Boolean} preventDefault default true
524 * @cfg {Boolean} removeClass remove the standard class..
525 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
528 * Create a new button
529 * @param {Object} config The config object
533 Roo.bootstrap.Button = function(config){
534 Roo.bootstrap.Button.superclass.constructor.call(this, config);
539 * When a butotn is pressed
540 * @param {Roo.bootstrap.Button} this
541 * @param {Roo.EventObject} e
546 * After the button has been toggles
547 * @param {Roo.EventObject} e
548 * @param {boolean} pressed (also available as button.pressed)
554 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
572 preventDefault: true,
581 getAutoCreate : function(){
589 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
590 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
595 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
597 if (this.toggle == true) {
600 cls: 'slider-frame roo-button',
605 'data-off-text':'OFF',
606 cls: 'slider-button',
612 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
613 cfg.cls += ' '+this.weight;
622 cfg["aria-hidden"] = true;
624 cfg.html = "×";
630 if (this.theme==='default') {
631 cfg.cls = 'btn roo-button';
633 //if (this.parentType != 'Navbar') {
634 this.weight = this.weight.length ? this.weight : 'default';
636 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
638 cfg.cls += ' btn-' + this.weight;
640 } else if (this.theme==='glow') {
643 cfg.cls = 'btn-glow roo-button';
645 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
647 cfg.cls += ' ' + this.weight;
653 this.cls += ' inverse';
658 cfg.cls += ' active';
662 cfg.disabled = 'disabled';
666 Roo.log('changing to ul' );
668 this.glyphicon = 'caret';
671 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
673 //gsRoo.log(this.parentType);
674 if (this.parentType === 'Navbar' && !this.parent().bar) {
675 Roo.log('changing to li?');
684 href : this.href || '#'
687 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
688 cfg.cls += ' dropdown';
695 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
697 if (this.glyphicon) {
698 cfg.html = ' ' + cfg.html;
703 cls: 'glyphicon glyphicon-' + this.glyphicon
713 // cfg.cls='btn roo-button';
717 var value = cfg.html;
722 cls: 'glyphicon glyphicon-' + this.glyphicon,
741 cfg.cls += ' dropdown';
742 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
745 if (cfg.tag !== 'a' && this.href !== '') {
746 throw "Tag must be a to set href.";
747 } else if (this.href.length > 0) {
748 cfg.href = this.href;
751 if(this.removeClass){
756 cfg.target = this.target;
761 initEvents: function() {
762 // Roo.log('init events?');
763 // Roo.log(this.el.dom);
766 if (typeof (this.menu) != 'undefined') {
767 this.menu.parentType = this.xtype;
768 this.menu.triggerEl = this.el;
769 this.addxtype(Roo.apply({}, this.menu));
773 if (this.el.hasClass('roo-button')) {
774 this.el.on('click', this.onClick, this);
776 this.el.select('.roo-button').on('click', this.onClick, this);
779 if(this.removeClass){
780 this.el.on('click', this.onClick, this);
783 this.el.enableDisplayMode();
786 onClick : function(e)
793 Roo.log('button on click ');
794 if(this.preventDefault){
797 if (this.pressed === true || this.pressed === false) {
798 this.pressed = !this.pressed;
799 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
800 this.fireEvent('toggle', this, e, this.pressed);
804 this.fireEvent('click', this, e);
808 * Enables this button
812 this.disabled = false;
813 this.el.removeClass('disabled');
817 * Disable this button
821 this.disabled = true;
822 this.el.addClass('disabled');
825 * sets the active state on/off,
826 * @param {Boolean} state (optional) Force a particular state
828 setActive : function(v) {
830 this.el[v ? 'addClass' : 'removeClass']('active');
833 * toggles the current active state
835 toggleActive : function()
837 var active = this.el.hasClass('active');
838 this.setActive(!active);
842 setText : function(str)
844 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
848 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
871 * @class Roo.bootstrap.Column
872 * @extends Roo.bootstrap.Component
873 * Bootstrap Column class
874 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
875 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
876 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
877 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
878 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
879 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
880 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
881 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
884 * @cfg {Boolean} hidden (true|false) hide the element
885 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
886 * @cfg {String} fa (ban|check|...) font awesome icon
887 * @cfg {Number} fasize (1|2|....) font awsome size
889 * @cfg {String} icon (info-sign|check|...) glyphicon name
891 * @cfg {String} html content of column.
894 * Create a new Column
895 * @param {Object} config The config object
898 Roo.bootstrap.Column = function(config){
899 Roo.bootstrap.Column.superclass.constructor.call(this, config);
902 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
920 getAutoCreate : function(){
921 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
929 ['xs','sm','md','lg'].map(function(size){
930 //Roo.log( size + ':' + settings[size]);
932 if (settings[size+'off'] !== false) {
933 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
936 if (settings[size] === false) {
940 if (!settings[size]) { // 0 = hidden
941 cfg.cls += ' hidden-' + size;
944 cfg.cls += ' col-' + size + '-' + settings[size];
949 cfg.cls += ' hidden';
952 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
953 cfg.cls +=' alert alert-' + this.alert;
957 if (this.html.length) {
958 cfg.html = this.html;
962 if (this.fasize > 1) {
963 fasize = ' fa-' + this.fasize + 'x';
965 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
970 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
989 * @class Roo.bootstrap.Container
990 * @extends Roo.bootstrap.Component
991 * Bootstrap Container class
992 * @cfg {Boolean} jumbotron is it a jumbotron element
993 * @cfg {String} html content of element
994 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
995 * @cfg {String} panel (primary|success|info|warning|danger) render as panel - type - primary/success.....
996 * @cfg {String} header content of header (for panel)
997 * @cfg {String} footer content of footer (for panel)
998 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
999 * @cfg {String} tag (header|aside|section) type of HTML tag.
1000 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1001 * @cfg {String} fa font awesome icon
1002 * @cfg {String} icon (info-sign|check|...) glyphicon name
1003 * @cfg {Boolean} hidden (true|false) hide the element
1004 * @cfg {Boolean} expandable (true|false) default false
1005 * @cfg {Boolean} expanded (true|false) default true
1006 * @cfg {String} rheader contet on the right of header
1007 * @cfg {Boolean} clickable (true|false) default false
1011 * Create a new Container
1012 * @param {Object} config The config object
1015 Roo.bootstrap.Container = function(config){
1016 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1022 * After the panel has been expand
1024 * @param {Roo.bootstrap.Container} this
1029 * After the panel has been collapsed
1031 * @param {Roo.bootstrap.Container} this
1036 * When a element is chick
1037 * @param {Roo.bootstrap.Container} this
1038 * @param {Roo.EventObject} e
1044 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1062 getChildContainer : function() {
1068 if (this.panel.length) {
1069 return this.el.select('.panel-body',true).first();
1076 getAutoCreate : function(){
1079 tag : this.tag || 'div',
1083 if (this.jumbotron) {
1084 cfg.cls = 'jumbotron';
1089 // - this is applied by the parent..
1091 // cfg.cls = this.cls + '';
1094 if (this.sticky.length) {
1096 var bd = Roo.get(document.body);
1097 if (!bd.hasClass('bootstrap-sticky')) {
1098 bd.addClass('bootstrap-sticky');
1099 Roo.select('html',true).setStyle('height', '100%');
1102 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1106 if (this.well.length) {
1107 switch (this.well) {
1110 cfg.cls +=' well well-' +this.well;
1119 cfg.cls += ' hidden';
1123 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1124 cfg.cls +=' alert alert-' + this.alert;
1129 if (this.panel.length) {
1130 cfg.cls += ' panel panel-' + this.panel;
1132 if (this.header.length) {
1136 if(this.expandable){
1138 cfg.cls = cfg.cls + ' expandable';
1142 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1150 cls : 'panel-title',
1151 html : (this.expandable ? ' ' : '') + this.header
1155 cls: 'panel-header-right',
1161 cls : 'panel-heading',
1162 style : this.expandable ? 'cursor: pointer' : '',
1170 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1175 if (this.footer.length) {
1177 cls : 'panel-footer',
1186 body.html = this.html || cfg.html;
1187 // prefix with the icons..
1189 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1192 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1197 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1198 cfg.cls = 'container';
1204 initEvents: function()
1206 if(this.expandable){
1207 var headerEl = this.headerEl();
1210 headerEl.on('click', this.onToggleClick, this);
1215 this.el.on('click', this.onClick, this);
1220 onToggleClick : function()
1222 var headerEl = this.headerEl();
1238 if(this.fireEvent('expand', this)) {
1240 this.expanded = true;
1242 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1244 this.el.select('.panel-body',true).first().removeClass('hide');
1246 var toggleEl = this.toggleEl();
1252 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1257 collapse : function()
1259 if(this.fireEvent('collapse', this)) {
1261 this.expanded = false;
1263 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1264 this.el.select('.panel-body',true).first().addClass('hide');
1266 var toggleEl = this.toggleEl();
1272 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1276 toggleEl : function()
1278 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1282 return this.el.select('.panel-heading .fa',true).first();
1285 headerEl : function()
1287 if(!this.el || !this.panel.length || !this.header.length){
1291 return this.el.select('.panel-heading',true).first()
1294 titleEl : function()
1296 if(!this.el || !this.panel.length || !this.header.length){
1300 return this.el.select('.panel-title',true).first();
1303 setTitle : function(v)
1305 var titleEl = this.titleEl();
1311 titleEl.dom.innerHTML = v;
1314 getTitle : function()
1317 var titleEl = this.titleEl();
1323 return titleEl.dom.innerHTML;
1326 setRightTitle : function(v)
1328 var t = this.el.select('.panel-header-right',true).first();
1334 t.dom.innerHTML = v;
1337 onClick : function(e)
1341 this.fireEvent('click', this, e);
1355 * @class Roo.bootstrap.Img
1356 * @extends Roo.bootstrap.Component
1357 * Bootstrap Img class
1358 * @cfg {Boolean} imgResponsive false | true
1359 * @cfg {String} border rounded | circle | thumbnail
1360 * @cfg {String} src image source
1361 * @cfg {String} alt image alternative text
1362 * @cfg {String} href a tag href
1363 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1364 * @cfg {String} xsUrl xs image source
1365 * @cfg {String} smUrl sm image source
1366 * @cfg {String} mdUrl md image source
1367 * @cfg {String} lgUrl lg image source
1370 * Create a new Input
1371 * @param {Object} config The config object
1374 Roo.bootstrap.Img = function(config){
1375 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1381 * The img click event for the img.
1382 * @param {Roo.EventObject} e
1388 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1390 imgResponsive: true,
1400 getAutoCreate : function()
1402 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1403 return this.createSingleImg();
1408 cls: 'roo-image-responsive-group',
1413 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1415 if(!_this[size + 'Url']){
1421 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1422 html: _this.html || cfg.html,
1423 src: _this[size + 'Url']
1426 img.cls += ' roo-image-responsive-' + size;
1428 var s = ['xs', 'sm', 'md', 'lg'];
1430 s.splice(s.indexOf(size), 1);
1432 Roo.each(s, function(ss){
1433 img.cls += ' hidden-' + ss;
1436 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1437 cfg.cls += ' img-' + _this.border;
1441 cfg.alt = _this.alt;
1454 a.target = _this.target;
1458 cfg.cn.push((_this.href) ? a : img);
1465 createSingleImg : function()
1469 cls: (this.imgResponsive) ? 'img-responsive' : '',
1471 src : 'about:blank' // just incase src get's set to undefined?!?
1474 cfg.html = this.html || cfg.html;
1476 cfg.src = this.src || cfg.src;
1478 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1479 cfg.cls += ' img-' + this.border;
1496 a.target = this.target;
1501 return (this.href) ? a : cfg;
1504 initEvents: function()
1507 this.el.on('click', this.onClick, this);
1512 onClick : function(e)
1514 Roo.log('img onclick');
1515 this.fireEvent('click', this, e);
1518 * Sets the url of the image - used to update it
1519 * @param {String} url the url of the image
1522 setSrc : function(url)
1525 this.el.select('img', true).first().dom.src = url;
1541 * @class Roo.bootstrap.Link
1542 * @extends Roo.bootstrap.Component
1543 * Bootstrap Link Class
1544 * @cfg {String} alt image alternative text
1545 * @cfg {String} href a tag href
1546 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1547 * @cfg {String} html the content of the link.
1548 * @cfg {String} anchor name for the anchor link
1549 * @cfg {String} fa - favicon
1551 * @cfg {Boolean} preventDefault (true | false) default false
1555 * Create a new Input
1556 * @param {Object} config The config object
1559 Roo.bootstrap.Link = function(config){
1560 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1566 * The img click event for the img.
1567 * @param {Roo.EventObject} e
1573 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1577 preventDefault: false,
1583 getAutoCreate : function()
1585 var html = this.html || '';
1587 if (this.fa !== false) {
1588 html = '<i class="fa fa-' + this.fa + '"></i>';
1593 // anchor's do not require html/href...
1594 if (this.anchor === false) {
1596 cfg.href = this.href || '#';
1598 cfg.name = this.anchor;
1599 if (this.html !== false || this.fa !== false) {
1602 if (this.href !== false) {
1603 cfg.href = this.href;
1607 if(this.alt !== false){
1612 if(this.target !== false) {
1613 cfg.target = this.target;
1619 initEvents: function() {
1621 if(!this.href || this.preventDefault){
1622 this.el.on('click', this.onClick, this);
1626 onClick : function(e)
1628 if(this.preventDefault){
1631 //Roo.log('img onclick');
1632 this.fireEvent('click', this, e);
1645 * @class Roo.bootstrap.Header
1646 * @extends Roo.bootstrap.Component
1647 * Bootstrap Header class
1648 * @cfg {String} html content of header
1649 * @cfg {Number} level (1|2|3|4|5|6) default 1
1652 * Create a new Header
1653 * @param {Object} config The config object
1657 Roo.bootstrap.Header = function(config){
1658 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1661 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1669 getAutoCreate : function(){
1674 tag: 'h' + (1 *this.level),
1675 html: this.html || ''
1687 * Ext JS Library 1.1.1
1688 * Copyright(c) 2006-2007, Ext JS, LLC.
1690 * Originally Released Under LGPL - original licence link has changed is not relivant.
1693 * <script type="text/javascript">
1697 * @class Roo.bootstrap.MenuMgr
1698 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1701 Roo.bootstrap.MenuMgr = function(){
1702 var menus, active, groups = {}, attached = false, lastShow = new Date();
1704 // private - called when first menu is created
1707 active = new Roo.util.MixedCollection();
1708 Roo.get(document).addKeyListener(27, function(){
1709 if(active.length > 0){
1717 if(active && active.length > 0){
1718 var c = active.clone();
1728 if(active.length < 1){
1729 Roo.get(document).un("mouseup", onMouseDown);
1737 var last = active.last();
1738 lastShow = new Date();
1741 Roo.get(document).on("mouseup", onMouseDown);
1746 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1747 m.parentMenu.activeChild = m;
1748 }else if(last && last.isVisible()){
1749 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1754 function onBeforeHide(m){
1756 m.activeChild.hide();
1758 if(m.autoHideTimer){
1759 clearTimeout(m.autoHideTimer);
1760 delete m.autoHideTimer;
1765 function onBeforeShow(m){
1766 var pm = m.parentMenu;
1767 if(!pm && !m.allowOtherMenus){
1769 }else if(pm && pm.activeChild && active != m){
1770 pm.activeChild.hide();
1774 // private this should really trigger on mouseup..
1775 function onMouseDown(e){
1776 Roo.log("on Mouse Up");
1778 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1779 Roo.log("MenuManager hideAll");
1788 function onBeforeCheck(mi, state){
1790 var g = groups[mi.group];
1791 for(var i = 0, l = g.length; i < l; i++){
1793 g[i].setChecked(false);
1802 * Hides all menus that are currently visible
1804 hideAll : function(){
1809 register : function(menu){
1813 menus[menu.id] = menu;
1814 menu.on("beforehide", onBeforeHide);
1815 menu.on("hide", onHide);
1816 menu.on("beforeshow", onBeforeShow);
1817 menu.on("show", onShow);
1819 if(g && menu.events["checkchange"]){
1823 groups[g].push(menu);
1824 menu.on("checkchange", onCheck);
1829 * Returns a {@link Roo.menu.Menu} object
1830 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1831 * be used to generate and return a new Menu instance.
1833 get : function(menu){
1834 if(typeof menu == "string"){ // menu id
1836 }else if(menu.events){ // menu instance
1839 /*else if(typeof menu.length == 'number'){ // array of menu items?
1840 return new Roo.bootstrap.Menu({items:menu});
1841 }else{ // otherwise, must be a config
1842 return new Roo.bootstrap.Menu(menu);
1849 unregister : function(menu){
1850 delete menus[menu.id];
1851 menu.un("beforehide", onBeforeHide);
1852 menu.un("hide", onHide);
1853 menu.un("beforeshow", onBeforeShow);
1854 menu.un("show", onShow);
1856 if(g && menu.events["checkchange"]){
1857 groups[g].remove(menu);
1858 menu.un("checkchange", onCheck);
1863 registerCheckable : function(menuItem){
1864 var g = menuItem.group;
1869 groups[g].push(menuItem);
1870 menuItem.on("beforecheckchange", onBeforeCheck);
1875 unregisterCheckable : function(menuItem){
1876 var g = menuItem.group;
1878 groups[g].remove(menuItem);
1879 menuItem.un("beforecheckchange", onBeforeCheck);
1891 * @class Roo.bootstrap.Menu
1892 * @extends Roo.bootstrap.Component
1893 * Bootstrap Menu class - container for MenuItems
1894 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1895 * @cfg {bool} hidden if the menu should be hidden when rendered.
1896 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1897 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1901 * @param {Object} config The config object
1905 Roo.bootstrap.Menu = function(config){
1906 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1907 if (this.registerMenu && this.type != 'treeview') {
1908 Roo.bootstrap.MenuMgr.register(this);
1913 * Fires before this menu is displayed
1914 * @param {Roo.menu.Menu} this
1919 * Fires before this menu is hidden
1920 * @param {Roo.menu.Menu} this
1925 * Fires after this menu is displayed
1926 * @param {Roo.menu.Menu} this
1931 * Fires after this menu is hidden
1932 * @param {Roo.menu.Menu} this
1937 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1938 * @param {Roo.menu.Menu} this
1939 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1940 * @param {Roo.EventObject} e
1945 * Fires when the mouse is hovering over this menu
1946 * @param {Roo.menu.Menu} this
1947 * @param {Roo.EventObject} e
1948 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1953 * Fires when the mouse exits this menu
1954 * @param {Roo.menu.Menu} this
1955 * @param {Roo.EventObject} e
1956 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1961 * Fires when a menu item contained in this menu is clicked
1962 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1963 * @param {Roo.EventObject} e
1967 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1970 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1974 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1977 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1979 registerMenu : true,
1981 menuItems :false, // stores the menu items..
1991 getChildContainer : function() {
1995 getAutoCreate : function(){
1997 //if (['right'].indexOf(this.align)!==-1) {
1998 // cfg.cn[1].cls += ' pull-right'
2004 cls : 'dropdown-menu' ,
2005 style : 'z-index:1000'
2009 if (this.type === 'submenu') {
2010 cfg.cls = 'submenu active';
2012 if (this.type === 'treeview') {
2013 cfg.cls = 'treeview-menu';
2018 initEvents : function() {
2020 // Roo.log("ADD event");
2021 // Roo.log(this.triggerEl.dom);
2023 this.triggerEl.on('click', this.onTriggerClick, this);
2025 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2027 this.triggerEl.addClass('dropdown-toggle');
2030 this.el.on('touchstart' , this.onTouch, this);
2032 this.el.on('click' , this.onClick, this);
2034 this.el.on("mouseover", this.onMouseOver, this);
2035 this.el.on("mouseout", this.onMouseOut, this);
2039 findTargetItem : function(e)
2041 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2045 //Roo.log(t); Roo.log(t.id);
2047 //Roo.log(this.menuitems);
2048 return this.menuitems.get(t.id);
2050 //return this.items.get(t.menuItemId);
2056 onTouch : function(e)
2058 Roo.log("menu.onTouch");
2059 //e.stopEvent(); this make the user popdown broken
2063 onClick : function(e)
2065 Roo.log("menu.onClick");
2067 var t = this.findTargetItem(e);
2068 if(!t || t.isContainer){
2073 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2074 if(t == this.activeItem && t.shouldDeactivate(e)){
2075 this.activeItem.deactivate();
2076 delete this.activeItem;
2080 this.setActiveItem(t, true);
2088 Roo.log('pass click event');
2092 this.fireEvent("click", this, t, e);
2096 (function() { _this.hide(); }).defer(500);
2099 onMouseOver : function(e){
2100 var t = this.findTargetItem(e);
2103 // if(t.canActivate && !t.disabled){
2104 // this.setActiveItem(t, true);
2108 this.fireEvent("mouseover", this, e, t);
2110 isVisible : function(){
2111 return !this.hidden;
2113 onMouseOut : function(e){
2114 var t = this.findTargetItem(e);
2117 // if(t == this.activeItem && t.shouldDeactivate(e)){
2118 // this.activeItem.deactivate();
2119 // delete this.activeItem;
2122 this.fireEvent("mouseout", this, e, t);
2127 * Displays this menu relative to another element
2128 * @param {String/HTMLElement/Roo.Element} element The element to align to
2129 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2130 * the element (defaults to this.defaultAlign)
2131 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2133 show : function(el, pos, parentMenu){
2134 this.parentMenu = parentMenu;
2138 this.fireEvent("beforeshow", this);
2139 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2142 * Displays this menu at a specific xy position
2143 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2144 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2146 showAt : function(xy, parentMenu, /* private: */_e){
2147 this.parentMenu = parentMenu;
2152 this.fireEvent("beforeshow", this);
2153 //xy = this.el.adjustForConstraints(xy);
2157 this.hideMenuItems();
2158 this.hidden = false;
2159 this.triggerEl.addClass('open');
2161 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2162 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2165 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2170 this.fireEvent("show", this);
2176 this.doFocus.defer(50, this);
2180 doFocus : function(){
2182 this.focusEl.focus();
2187 * Hides this menu and optionally all parent menus
2188 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2190 hide : function(deep)
2193 this.hideMenuItems();
2194 if(this.el && this.isVisible()){
2195 this.fireEvent("beforehide", this);
2196 if(this.activeItem){
2197 this.activeItem.deactivate();
2198 this.activeItem = null;
2200 this.triggerEl.removeClass('open');;
2202 this.fireEvent("hide", this);
2204 if(deep === true && this.parentMenu){
2205 this.parentMenu.hide(true);
2209 onTriggerClick : function(e)
2211 Roo.log('trigger click');
2213 var target = e.getTarget();
2215 Roo.log(target.nodeName.toLowerCase());
2217 if(target.nodeName.toLowerCase() === 'i'){
2223 onTriggerPress : function(e)
2225 Roo.log('trigger press');
2226 //Roo.log(e.getTarget());
2227 // Roo.log(this.triggerEl.dom);
2229 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2230 var pel = Roo.get(e.getTarget());
2231 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2232 Roo.log('is treeview or dropdown?');
2236 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2240 if (this.isVisible()) {
2245 this.show(this.triggerEl, false, false);
2248 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2255 hideMenuItems : function()
2257 Roo.log("hide Menu Items");
2261 //$(backdrop).remove()
2262 this.el.select('.open',true).each(function(aa) {
2264 aa.removeClass('open');
2265 //var parent = getParent($(this))
2266 //var relatedTarget = { relatedTarget: this }
2268 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2269 //if (e.isDefaultPrevented()) return
2270 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2273 addxtypeChild : function (tree, cntr) {
2274 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2276 this.menuitems.add(comp);
2297 * @class Roo.bootstrap.MenuItem
2298 * @extends Roo.bootstrap.Component
2299 * Bootstrap MenuItem class
2300 * @cfg {String} html the menu label
2301 * @cfg {String} href the link
2302 * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2303 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2304 * @cfg {Boolean} active used on sidebars to highlight active itesm
2305 * @cfg {String} fa favicon to show on left of menu item.
2306 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2310 * Create a new MenuItem
2311 * @param {Object} config The config object
2315 Roo.bootstrap.MenuItem = function(config){
2316 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2321 * The raw click event for the entire grid.
2322 * @param {Roo.bootstrap.MenuItem} this
2323 * @param {Roo.EventObject} e
2329 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2333 preventDefault: true,
2334 isContainer : false,
2338 getAutoCreate : function(){
2340 if(this.isContainer){
2343 cls: 'dropdown-menu-item'
2357 if (this.fa !== false) {
2360 cls : 'fa fa-' + this.fa
2369 cls: 'dropdown-menu-item',
2372 if (this.parent().type == 'treeview') {
2373 cfg.cls = 'treeview-menu';
2376 cfg.cls += ' active';
2381 anc.href = this.href || cfg.cn[0].href ;
2382 ctag.html = this.html || cfg.cn[0].html ;
2386 initEvents: function()
2388 if (this.parent().type == 'treeview') {
2389 this.el.select('a').on('click', this.onClick, this);
2392 this.menu.parentType = this.xtype;
2393 this.menu.triggerEl = this.el;
2394 this.menu = this.addxtype(Roo.apply({}, this.menu));
2398 onClick : function(e)
2400 Roo.log('item on click ');
2401 //if(this.preventDefault){
2402 // e.preventDefault();
2404 //this.parent().hideMenuItems();
2406 this.fireEvent('click', this, e);
2425 * @class Roo.bootstrap.MenuSeparator
2426 * @extends Roo.bootstrap.Component
2427 * Bootstrap MenuSeparator class
2430 * Create a new MenuItem
2431 * @param {Object} config The config object
2435 Roo.bootstrap.MenuSeparator = function(config){
2436 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2439 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2441 getAutoCreate : function(){
2460 * @class Roo.bootstrap.Modal
2461 * @extends Roo.bootstrap.Component
2462 * Bootstrap Modal class
2463 * @cfg {String} title Title of dialog
2464 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2465 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2466 * @cfg {Boolean} specificTitle default false
2467 * @cfg {Array} buttons Array of buttons or standard button set..
2468 * @cfg {String} buttonPosition (left|right|center) default right
2469 * @cfg {Boolean} animate default true
2470 * @cfg {Boolean} allow_close default true
2471 * @cfg {Boolean} fitwindow default false
2472 * @cfg {String} size (sm|lg) default empty
2476 * Create a new Modal Dialog
2477 * @param {Object} config The config object
2480 Roo.bootstrap.Modal = function(config){
2481 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2486 * The raw btnclick event for the button
2487 * @param {Roo.EventObject} e
2491 this.buttons = this.buttons || [];
2494 this.tmpl = Roo.factory(this.tmpl);
2499 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2501 title : 'test dialog',
2511 specificTitle: false,
2513 buttonPosition: 'right',
2532 onRender : function(ct, position)
2534 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2537 var cfg = Roo.apply({}, this.getAutoCreate());
2540 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2542 //if (!cfg.name.length) {
2546 cfg.cls += ' ' + this.cls;
2549 cfg.style = this.style;
2551 this.el = Roo.get(document.body).createChild(cfg, position);
2553 //var type = this.el.dom.type;
2556 if(this.tabIndex !== undefined){
2557 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2560 this.dialogEl = this.el.select('.modal-dialog',true).first();
2561 this.bodyEl = this.el.select('.modal-body',true).first();
2562 this.closeEl = this.el.select('.modal-header .close', true).first();
2563 this.footerEl = this.el.select('.modal-footer',true).first();
2564 this.titleEl = this.el.select('.modal-title',true).first();
2568 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2569 this.maskEl.enableDisplayMode("block");
2571 //this.el.addClass("x-dlg-modal");
2573 if (this.buttons.length) {
2574 Roo.each(this.buttons, function(bb) {
2575 var b = Roo.apply({}, bb);
2576 b.xns = b.xns || Roo.bootstrap;
2577 b.xtype = b.xtype || 'Button';
2578 if (typeof(b.listeners) == 'undefined') {
2579 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2582 var btn = Roo.factory(b);
2584 btn.render(this.el.select('.modal-footer div').first());
2588 // render the children.
2591 if(typeof(this.items) != 'undefined'){
2592 var items = this.items;
2595 for(var i =0;i < items.length;i++) {
2596 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2600 this.items = nitems;
2602 // where are these used - they used to be body/close/footer
2606 //this.el.addClass([this.fieldClass, this.cls]);
2610 getAutoCreate : function(){
2615 html : this.html || ''
2620 cls : 'modal-title',
2624 if(this.specificTitle){
2630 if (this.allow_close) {
2642 if(this.size.length){
2643 size = 'modal-' + this.size;
2648 style : 'display: none',
2651 cls: "modal-dialog " + size,
2654 cls : "modal-content",
2657 cls : 'modal-header',
2662 cls : 'modal-footer',
2666 cls: 'btn-' + this.buttonPosition
2683 modal.cls += ' fade';
2689 getChildContainer : function() {
2694 getButtonContainer : function() {
2695 return this.el.select('.modal-footer div',true).first();
2698 initEvents : function()
2700 if (this.allow_close) {
2701 this.closeEl.on('click', this.hide, this);
2703 Roo.EventManager.onWindowResize(this.resize, this, true);
2710 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2711 if (this.fitwindow) {
2712 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2713 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2718 setSize : function(w,h)
2728 if (!this.rendered) {
2732 this.el.setStyle('display', 'block');
2734 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2737 this.el.addClass('in');
2740 this.el.addClass('in');
2744 // not sure how we can show data in here..
2746 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2749 Roo.get(document.body).addClass("x-body-masked");
2750 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2752 this.el.setStyle('zIndex', '10001');
2754 this.fireEvent('show', this);
2759 this.items.forEach( function(e) {
2760 e.layout ? e.layout() : false;
2769 Roo.get(document.body).removeClass("x-body-masked");
2770 this.el.removeClass('in');
2771 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2773 if(this.animate){ // why
2775 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2777 this.el.setStyle('display', 'none');
2780 this.fireEvent('hide', this);
2783 addButton : function(str, cb)
2787 var b = Roo.apply({}, { html : str } );
2788 b.xns = b.xns || Roo.bootstrap;
2789 b.xtype = b.xtype || 'Button';
2790 if (typeof(b.listeners) == 'undefined') {
2791 b.listeners = { click : cb.createDelegate(this) };
2794 var btn = Roo.factory(b);
2796 btn.render(this.el.select('.modal-footer div').first());
2802 setDefaultButton : function(btn)
2804 //this.el.select('.modal-footer').()
2808 resizeTo: function(w,h)
2812 this.dialogEl.setWidth(w);
2813 if (this.diff === false) {
2814 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2817 this.bodyEl.setHeight(h-this.diff);
2821 setContentSize : function(w, h)
2825 onButtonClick: function(btn,e)
2828 this.fireEvent('btnclick', btn.name, e);
2831 * Set the title of the Dialog
2832 * @param {String} str new Title
2834 setTitle: function(str) {
2835 this.titleEl.dom.innerHTML = str;
2838 * Set the body of the Dialog
2839 * @param {String} str new Title
2841 setBody: function(str) {
2842 this.bodyEl.dom.innerHTML = str;
2845 * Set the body of the Dialog using the template
2846 * @param {Obj} data - apply this data to the template and replace the body contents.
2848 applyBody: function(obj)
2851 Roo.log("Error - using apply Body without a template");
2854 this.tmpl.overwrite(this.bodyEl, obj);
2860 Roo.apply(Roo.bootstrap.Modal, {
2862 * Button config that displays a single OK button
2871 * Button config that displays Yes and No buttons
2887 * Button config that displays OK and Cancel buttons
2902 * Button config that displays Yes, No and Cancel buttons
2925 * messagebox - can be used as a replace
2929 * @class Roo.MessageBox
2930 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2934 Roo.Msg.alert('Status', 'Changes saved successfully.');
2936 // Prompt for user data:
2937 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2939 // process text value...
2943 // Show a dialog using config options:
2945 title:'Save Changes?',
2946 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2947 buttons: Roo.Msg.YESNOCANCEL,
2954 Roo.bootstrap.MessageBox = function(){
2955 var dlg, opt, mask, waitTimer;
2956 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2957 var buttons, activeTextEl, bwidth;
2961 var handleButton = function(button){
2963 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2967 var handleHide = function(){
2969 dlg.el.removeClass(opt.cls);
2972 // Roo.TaskMgr.stop(waitTimer);
2973 // waitTimer = null;
2978 var updateButtons = function(b){
2981 buttons["ok"].hide();
2982 buttons["cancel"].hide();
2983 buttons["yes"].hide();
2984 buttons["no"].hide();
2985 //dlg.footer.dom.style.display = 'none';
2988 dlg.footerEl.dom.style.display = '';
2989 for(var k in buttons){
2990 if(typeof buttons[k] != "function"){
2993 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2994 width += buttons[k].el.getWidth()+15;
3004 var handleEsc = function(d, k, e){
3005 if(opt && opt.closable !== false){
3015 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3016 * @return {Roo.BasicDialog} The BasicDialog element
3018 getDialog : function(){
3020 dlg = new Roo.bootstrap.Modal( {
3023 //constraintoviewport:false,
3025 //collapsible : false,
3030 //buttonAlign:"center",
3031 closeClick : function(){
3032 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3035 handleButton("cancel");
3040 dlg.on("hide", handleHide);
3042 //dlg.addKeyListener(27, handleEsc);
3044 this.buttons = buttons;
3045 var bt = this.buttonText;
3046 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3047 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3048 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3049 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3051 bodyEl = dlg.bodyEl.createChild({
3053 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3054 '<textarea class="roo-mb-textarea"></textarea>' +
3055 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3057 msgEl = bodyEl.dom.firstChild;
3058 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3059 textboxEl.enableDisplayMode();
3060 textboxEl.addKeyListener([10,13], function(){
3061 if(dlg.isVisible() && opt && opt.buttons){
3064 }else if(opt.buttons.yes){
3065 handleButton("yes");
3069 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3070 textareaEl.enableDisplayMode();
3071 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3072 progressEl.enableDisplayMode();
3073 var pf = progressEl.dom.firstChild;
3075 pp = Roo.get(pf.firstChild);
3076 pp.setHeight(pf.offsetHeight);
3084 * Updates the message box body text
3085 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3086 * the XHTML-compliant non-breaking space character '&#160;')
3087 * @return {Roo.MessageBox} This message box
3089 updateText : function(text){
3090 if(!dlg.isVisible() && !opt.width){
3091 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3093 msgEl.innerHTML = text || ' ';
3095 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3096 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3098 Math.min(opt.width || cw , this.maxWidth),
3099 Math.max(opt.minWidth || this.minWidth, bwidth)
3102 activeTextEl.setWidth(w);
3104 if(dlg.isVisible()){
3105 dlg.fixedcenter = false;
3107 // to big, make it scroll. = But as usual stupid IE does not support
3110 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3111 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3112 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3114 bodyEl.dom.style.height = '';
3115 bodyEl.dom.style.overflowY = '';
3118 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3120 bodyEl.dom.style.overflowX = '';
3123 dlg.setContentSize(w, bodyEl.getHeight());
3124 if(dlg.isVisible()){
3125 dlg.fixedcenter = true;
3131 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3132 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3133 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3134 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3135 * @return {Roo.MessageBox} This message box
3137 updateProgress : function(value, text){
3139 this.updateText(text);
3141 if (pp) { // weird bug on my firefox - for some reason this is not defined
3142 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3148 * Returns true if the message box is currently displayed
3149 * @return {Boolean} True if the message box is visible, else false
3151 isVisible : function(){
3152 return dlg && dlg.isVisible();
3156 * Hides the message box if it is displayed
3159 if(this.isVisible()){
3165 * Displays a new message box, or reinitializes an existing message box, based on the config options
3166 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3167 * The following config object properties are supported:
3169 Property Type Description
3170 ---------- --------------- ------------------------------------------------------------------------------------
3171 animEl String/Element An id or Element from which the message box should animate as it opens and
3172 closes (defaults to undefined)
3173 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3174 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3175 closable Boolean False to hide the top-right close button (defaults to true). Note that
3176 progress and wait dialogs will ignore this property and always hide the
3177 close button as they can only be closed programmatically.
3178 cls String A custom CSS class to apply to the message box element
3179 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3180 displayed (defaults to 75)
3181 fn Function A callback function to execute after closing the dialog. The arguments to the
3182 function will be btn (the name of the button that was clicked, if applicable,
3183 e.g. "ok"), and text (the value of the active text field, if applicable).
3184 Progress and wait dialogs will ignore this option since they do not respond to
3185 user actions and can only be closed programmatically, so any required function
3186 should be called by the same code after it closes the dialog.
3187 icon String A CSS class that provides a background image to be used as an icon for
3188 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3189 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3190 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3191 modal Boolean False to allow user interaction with the page while the message box is
3192 displayed (defaults to true)
3193 msg String A string that will replace the existing message box body text (defaults
3194 to the XHTML-compliant non-breaking space character ' ')
3195 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3196 progress Boolean True to display a progress bar (defaults to false)
3197 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3198 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3199 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3200 title String The title text
3201 value String The string value to set into the active textbox element if displayed
3202 wait Boolean True to display a progress bar (defaults to false)
3203 width Number The width of the dialog in pixels
3210 msg: 'Please enter your address:',
3212 buttons: Roo.MessageBox.OKCANCEL,
3215 animEl: 'addAddressBtn'
3218 * @param {Object} config Configuration options
3219 * @return {Roo.MessageBox} This message box
3221 show : function(options)
3224 // this causes nightmares if you show one dialog after another
3225 // especially on callbacks..
3227 if(this.isVisible()){
3230 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3231 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3232 Roo.log("New Dialog Message:" + options.msg )
3233 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3234 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3237 var d = this.getDialog();
3239 d.setTitle(opt.title || " ");
3240 d.closeEl.setDisplayed(opt.closable !== false);
3241 activeTextEl = textboxEl;
3242 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3247 textareaEl.setHeight(typeof opt.multiline == "number" ?
3248 opt.multiline : this.defaultTextHeight);
3249 activeTextEl = textareaEl;
3258 progressEl.setDisplayed(opt.progress === true);
3259 this.updateProgress(0);
3260 activeTextEl.dom.value = opt.value || "";
3262 dlg.setDefaultButton(activeTextEl);
3264 var bs = opt.buttons;
3268 }else if(bs && bs.yes){
3269 db = buttons["yes"];
3271 dlg.setDefaultButton(db);
3273 bwidth = updateButtons(opt.buttons);
3274 this.updateText(opt.msg);
3276 d.el.addClass(opt.cls);
3278 d.proxyDrag = opt.proxyDrag === true;
3279 d.modal = opt.modal !== false;
3280 d.mask = opt.modal !== false ? mask : false;
3282 // force it to the end of the z-index stack so it gets a cursor in FF
3283 document.body.appendChild(dlg.el.dom);
3284 d.animateTarget = null;
3285 d.show(options.animEl);
3291 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3292 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3293 * and closing the message box when the process is complete.
3294 * @param {String} title The title bar text
3295 * @param {String} msg The message box body text
3296 * @return {Roo.MessageBox} This message box
3298 progress : function(title, msg){
3305 minWidth: this.minProgressWidth,
3312 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3313 * If a callback function is passed it will be called after the user clicks the button, and the
3314 * id of the button that was clicked will be passed as the only parameter to the callback
3315 * (could also be the top-right close button).
3316 * @param {String} title The title bar text
3317 * @param {String} msg The message box body text
3318 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3319 * @param {Object} scope (optional) The scope of the callback function
3320 * @return {Roo.MessageBox} This message box
3322 alert : function(title, msg, fn, scope){
3335 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3336 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3337 * You are responsible for closing the message box when the process is complete.
3338 * @param {String} msg The message box body text
3339 * @param {String} title (optional) The title bar text
3340 * @return {Roo.MessageBox} This message box
3342 wait : function(msg, title){
3353 waitTimer = Roo.TaskMgr.start({
3355 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3363 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3364 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3365 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3366 * @param {String} title The title bar text
3367 * @param {String} msg The message box body text
3368 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3369 * @param {Object} scope (optional) The scope of the callback function
3370 * @return {Roo.MessageBox} This message box
3372 confirm : function(title, msg, fn, scope){
3376 buttons: this.YESNO,
3385 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3386 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3387 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3388 * (could also be the top-right close button) and the text that was entered will be passed as the two
3389 * parameters to the callback.
3390 * @param {String} title The title bar text
3391 * @param {String} msg The message box body text
3392 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3393 * @param {Object} scope (optional) The scope of the callback function
3394 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3395 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3396 * @return {Roo.MessageBox} This message box
3398 prompt : function(title, msg, fn, scope, multiline){
3402 buttons: this.OKCANCEL,
3407 multiline: multiline,
3414 * Button config that displays a single OK button
3419 * Button config that displays Yes and No buttons
3422 YESNO : {yes:true, no:true},
3424 * Button config that displays OK and Cancel buttons
3427 OKCANCEL : {ok:true, cancel:true},
3429 * Button config that displays Yes, No and Cancel buttons
3432 YESNOCANCEL : {yes:true, no:true, cancel:true},
3435 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3438 defaultTextHeight : 75,
3440 * The maximum width in pixels of the message box (defaults to 600)
3445 * The minimum width in pixels of the message box (defaults to 100)
3450 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3451 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3454 minProgressWidth : 250,
3456 * An object containing the default button text strings that can be overriden for localized language support.
3457 * Supported properties are: ok, cancel, yes and no.
3458 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3471 * Shorthand for {@link Roo.MessageBox}
3473 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3474 Roo.Msg = Roo.Msg || Roo.MessageBox;
3483 * @class Roo.bootstrap.Navbar
3484 * @extends Roo.bootstrap.Component
3485 * Bootstrap Navbar class
3488 * Create a new Navbar
3489 * @param {Object} config The config object
3493 Roo.bootstrap.Navbar = function(config){
3494 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3498 * @event beforetoggle
3499 * Fire before toggle the menu
3500 * @param {Roo.EventObject} e
3502 "beforetoggle" : true
3506 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3515 getAutoCreate : function(){
3518 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3522 initEvents :function ()
3524 //Roo.log(this.el.select('.navbar-toggle',true));
3525 this.el.select('.navbar-toggle',true).on('click', function() {
3526 if(this.fireEvent('beforetoggle', this) !== false){
3527 this.el.select('.navbar-collapse',true).toggleClass('in');
3537 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3539 var size = this.el.getSize();
3540 this.maskEl.setSize(size.width, size.height);
3541 this.maskEl.enableDisplayMode("block");
3550 getChildContainer : function()
3552 if (this.el.select('.collapse').getCount()) {
3553 return this.el.select('.collapse',true).first();
3586 * @class Roo.bootstrap.NavSimplebar
3587 * @extends Roo.bootstrap.Navbar
3588 * Bootstrap Sidebar class
3590 * @cfg {Boolean} inverse is inverted color
3592 * @cfg {String} type (nav | pills | tabs)
3593 * @cfg {Boolean} arrangement stacked | justified
3594 * @cfg {String} align (left | right) alignment
3596 * @cfg {Boolean} main (true|false) main nav bar? default false
3597 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3599 * @cfg {String} tag (header|footer|nav|div) default is nav
3605 * Create a new Sidebar
3606 * @param {Object} config The config object
3610 Roo.bootstrap.NavSimplebar = function(config){
3611 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3614 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3630 getAutoCreate : function(){
3634 tag : this.tag || 'div',
3647 this.type = this.type || 'nav';
3648 if (['tabs','pills'].indexOf(this.type)!==-1) {
3649 cfg.cn[0].cls += ' nav-' + this.type
3653 if (this.type!=='nav') {
3654 Roo.log('nav type must be nav/tabs/pills')
3656 cfg.cn[0].cls += ' navbar-nav'
3662 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3663 cfg.cn[0].cls += ' nav-' + this.arrangement;
3667 if (this.align === 'right') {
3668 cfg.cn[0].cls += ' navbar-right';
3672 cfg.cls += ' navbar-inverse';
3699 * @class Roo.bootstrap.NavHeaderbar
3700 * @extends Roo.bootstrap.NavSimplebar
3701 * Bootstrap Sidebar class
3703 * @cfg {String} brand what is brand
3704 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3705 * @cfg {String} brand_href href of the brand
3706 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3707 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3708 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3709 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3712 * Create a new Sidebar
3713 * @param {Object} config The config object
3717 Roo.bootstrap.NavHeaderbar = function(config){
3718 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3722 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3729 desktopCenter : false,
3732 getAutoCreate : function(){
3735 tag: this.nav || 'nav',
3742 if (this.desktopCenter) {
3743 cn.push({cls : 'container', cn : []});
3750 cls: 'navbar-header',
3755 cls: 'navbar-toggle',
3756 'data-toggle': 'collapse',
3761 html: 'Toggle navigation'
3783 cls: 'collapse navbar-collapse',
3787 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3789 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3790 cfg.cls += ' navbar-' + this.position;
3792 // tag can override this..
3794 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3797 if (this.brand !== '') {
3800 href: this.brand_href ? this.brand_href : '#',
3801 cls: 'navbar-brand',
3809 cfg.cls += ' main-nav';
3817 getHeaderChildContainer : function()
3819 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3820 return this.el.select('.navbar-header',true).first();
3823 return this.getChildContainer();
3827 initEvents : function()
3829 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3831 if (this.autohide) {
3836 Roo.get(document).on('scroll',function(e) {
3837 var ns = Roo.get(document).getScroll().top;
3838 var os = prevScroll;
3842 ft.removeClass('slideDown');
3843 ft.addClass('slideUp');
3846 ft.removeClass('slideUp');
3847 ft.addClass('slideDown');
3868 * @class Roo.bootstrap.NavSidebar
3869 * @extends Roo.bootstrap.Navbar
3870 * Bootstrap Sidebar class
3873 * Create a new Sidebar
3874 * @param {Object} config The config object
3878 Roo.bootstrap.NavSidebar = function(config){
3879 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3882 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3884 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3886 getAutoCreate : function(){
3891 cls: 'sidebar sidebar-nav'
3913 * @class Roo.bootstrap.NavGroup
3914 * @extends Roo.bootstrap.Component
3915 * Bootstrap NavGroup class
3916 * @cfg {String} align (left|right)
3917 * @cfg {Boolean} inverse
3918 * @cfg {String} type (nav|pills|tab) default nav
3919 * @cfg {String} navId - reference Id for navbar.
3923 * Create a new nav group
3924 * @param {Object} config The config object
3927 Roo.bootstrap.NavGroup = function(config){
3928 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3931 Roo.bootstrap.NavGroup.register(this);
3935 * Fires when the active item changes
3936 * @param {Roo.bootstrap.NavGroup} this
3937 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3938 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3945 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3956 getAutoCreate : function()
3958 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3965 if (['tabs','pills'].indexOf(this.type)!==-1) {
3966 cfg.cls += ' nav-' + this.type
3968 if (this.type!=='nav') {
3969 Roo.log('nav type must be nav/tabs/pills')
3971 cfg.cls += ' navbar-nav'
3974 if (this.parent().sidebar) {
3977 cls: 'dashboard-menu sidebar-menu'
3983 if (this.form === true) {
3989 if (this.align === 'right') {
3990 cfg.cls += ' navbar-right';
3992 cfg.cls += ' navbar-left';
3996 if (this.align === 'right') {
3997 cfg.cls += ' navbar-right';
4001 cfg.cls += ' navbar-inverse';
4009 * sets the active Navigation item
4010 * @param {Roo.bootstrap.NavItem} the new current navitem
4012 setActiveItem : function(item)
4015 Roo.each(this.navItems, function(v){
4020 v.setActive(false, true);
4027 item.setActive(true, true);
4028 this.fireEvent('changed', this, item, prev);
4033 * gets the active Navigation item
4034 * @return {Roo.bootstrap.NavItem} the current navitem
4036 getActive : function()
4040 Roo.each(this.navItems, function(v){
4051 indexOfNav : function()
4055 Roo.each(this.navItems, function(v,i){
4066 * adds a Navigation item
4067 * @param {Roo.bootstrap.NavItem} the navitem to add
4069 addItem : function(cfg)
4071 var cn = new Roo.bootstrap.NavItem(cfg);
4073 cn.parentId = this.id;
4074 cn.onRender(this.el, null);
4078 * register a Navigation item
4079 * @param {Roo.bootstrap.NavItem} the navitem to add
4081 register : function(item)
4083 this.navItems.push( item);
4084 item.navId = this.navId;
4089 * clear all the Navigation item
4092 clearAll : function()
4095 this.el.dom.innerHTML = '';
4098 getNavItem: function(tabId)
4101 Roo.each(this.navItems, function(e) {
4102 if (e.tabId == tabId) {
4112 setActiveNext : function()
4114 var i = this.indexOfNav(this.getActive());
4115 if (i > this.navItems.length) {
4118 this.setActiveItem(this.navItems[i+1]);
4120 setActivePrev : function()
4122 var i = this.indexOfNav(this.getActive());
4126 this.setActiveItem(this.navItems[i-1]);
4128 clearWasActive : function(except) {
4129 Roo.each(this.navItems, function(e) {
4130 if (e.tabId != except.tabId && e.was_active) {
4131 e.was_active = false;
4138 getWasActive : function ()
4141 Roo.each(this.navItems, function(e) {
4156 Roo.apply(Roo.bootstrap.NavGroup, {
4160 * register a Navigation Group
4161 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4163 register : function(navgrp)
4165 this.groups[navgrp.navId] = navgrp;
4169 * fetch a Navigation Group based on the navigation ID
4170 * @param {string} the navgroup to add
4171 * @returns {Roo.bootstrap.NavGroup} the navgroup
4173 get: function(navId) {
4174 if (typeof(this.groups[navId]) == 'undefined') {
4176 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4178 return this.groups[navId] ;
4193 * @class Roo.bootstrap.NavItem
4194 * @extends Roo.bootstrap.Component
4195 * Bootstrap Navbar.NavItem class
4196 * @cfg {String} href link to
4197 * @cfg {String} html content of button
4198 * @cfg {String} badge text inside badge
4199 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4200 * @cfg {String} glyphicon name of glyphicon
4201 * @cfg {String} icon name of font awesome icon
4202 * @cfg {Boolean} active Is item active
4203 * @cfg {Boolean} disabled Is item disabled
4205 * @cfg {Boolean} preventDefault (true | false) default false
4206 * @cfg {String} tabId the tab that this item activates.
4207 * @cfg {String} tagtype (a|span) render as a href or span?
4208 * @cfg {Boolean} animateRef (true|false) link to element default false
4211 * Create a new Navbar Item
4212 * @param {Object} config The config object
4214 Roo.bootstrap.NavItem = function(config){
4215 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4220 * The raw click event for the entire grid.
4221 * @param {Roo.EventObject} e
4226 * Fires when the active item active state changes
4227 * @param {Roo.bootstrap.NavItem} this
4228 * @param {boolean} state the new state
4234 * Fires when scroll to element
4235 * @param {Roo.bootstrap.NavItem} this
4236 * @param {Object} options
4237 * @param {Roo.EventObject} e
4245 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4253 preventDefault : false,
4260 getAutoCreate : function(){
4269 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4271 if (this.disabled) {
4272 cfg.cls += ' disabled';
4275 if (this.href || this.html || this.glyphicon || this.icon) {
4279 href : this.href || "#",
4280 html: this.html || ''
4285 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4288 if(this.glyphicon) {
4289 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4294 cfg.cn[0].html += " <span class='caret'></span>";
4298 if (this.badge !== '') {
4300 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4308 initEvents: function()
4310 if (typeof (this.menu) != 'undefined') {
4311 this.menu.parentType = this.xtype;
4312 this.menu.triggerEl = this.el;
4313 this.menu = this.addxtype(Roo.apply({}, this.menu));
4316 this.el.select('a',true).on('click', this.onClick, this);
4318 if(this.tagtype == 'span'){
4319 this.el.select('span',true).on('click', this.onClick, this);
4322 // at this point parent should be available..
4323 this.parent().register(this);
4326 onClick : function(e)
4328 if (e.getTarget('.dropdown-menu-item')) {
4329 // did you click on a menu itemm.... - then don't trigger onclick..
4334 this.preventDefault ||
4337 Roo.log("NavItem - prevent Default?");
4341 if (this.disabled) {
4345 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4346 if (tg && tg.transition) {
4347 Roo.log("waiting for the transitionend");
4353 //Roo.log("fire event clicked");
4354 if(this.fireEvent('click', this, e) === false){
4358 if(this.tagtype == 'span'){
4362 //Roo.log(this.href);
4363 var ael = this.el.select('a',true).first();
4366 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4367 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4368 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4369 return; // ignore... - it's a 'hash' to another page.
4371 Roo.log("NavItem - prevent Default?");
4373 this.scrollToElement(e);
4377 var p = this.parent();
4379 if (['tabs','pills'].indexOf(p.type)!==-1) {
4380 if (typeof(p.setActiveItem) !== 'undefined') {
4381 p.setActiveItem(this);
4385 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4386 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4387 // remove the collapsed menu expand...
4388 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4392 isActive: function () {
4395 setActive : function(state, fire, is_was_active)
4397 if (this.active && !state && this.navId) {
4398 this.was_active = true;
4399 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4401 nv.clearWasActive(this);
4405 this.active = state;
4408 this.el.removeClass('active');
4409 } else if (!this.el.hasClass('active')) {
4410 this.el.addClass('active');
4413 this.fireEvent('changed', this, state);
4416 // show a panel if it's registered and related..
4418 if (!this.navId || !this.tabId || !state || is_was_active) {
4422 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4426 var pan = tg.getPanelByName(this.tabId);
4430 // if we can not flip to new panel - go back to old nav highlight..
4431 if (false == tg.showPanel(pan)) {
4432 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4434 var onav = nv.getWasActive();
4436 onav.setActive(true, false, true);
4445 // this should not be here...
4446 setDisabled : function(state)
4448 this.disabled = state;
4450 this.el.removeClass('disabled');
4451 } else if (!this.el.hasClass('disabled')) {
4452 this.el.addClass('disabled');
4458 * Fetch the element to display the tooltip on.
4459 * @return {Roo.Element} defaults to this.el
4461 tooltipEl : function()
4463 return this.el.select('' + this.tagtype + '', true).first();
4466 scrollToElement : function(e)
4468 var c = document.body;
4471 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4473 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4474 c = document.documentElement;
4477 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4483 var o = target.calcOffsetsTo(c);
4490 this.fireEvent('scrollto', this, options, e);
4492 Roo.get(c).scrollTo('top', options.value, true);
4505 * <span> icon </span>
4506 * <span> text </span>
4507 * <span>badge </span>
4511 * @class Roo.bootstrap.NavSidebarItem
4512 * @extends Roo.bootstrap.NavItem
4513 * Bootstrap Navbar.NavSidebarItem class
4514 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4515 * {bool} open is the menu open
4517 * Create a new Navbar Button
4518 * @param {Object} config The config object
4520 Roo.bootstrap.NavSidebarItem = function(config){
4521 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4526 * The raw click event for the entire grid.
4527 * @param {Roo.EventObject} e
4532 * Fires when the active item active state changes
4533 * @param {Roo.bootstrap.NavSidebarItem} this
4534 * @param {boolean} state the new state
4542 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4544 badgeWeight : 'default',
4548 getAutoCreate : function(){
4553 href : this.href || '#',
4565 html : this.html || ''
4570 cfg.cls += ' active';
4573 if (this.disabled) {
4574 cfg.cls += ' disabled';
4577 cfg.cls += ' open x-open';
4580 if (this.glyphicon || this.icon) {
4581 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4582 a.cn.push({ tag : 'i', cls : c }) ;
4587 if (this.badge !== '') {
4589 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4593 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4594 a.cls += 'dropdown-toggle treeview' ;
4602 initEvents : function()
4604 if (typeof (this.menu) != 'undefined') {
4605 this.menu.parentType = this.xtype;
4606 this.menu.triggerEl = this.el;
4607 this.menu = this.addxtype(Roo.apply({}, this.menu));
4610 this.el.on('click', this.onClick, this);
4613 if(this.badge !== ''){
4615 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4620 onClick : function(e)
4627 if(this.preventDefault){
4631 this.fireEvent('click', this);
4634 disable : function()
4636 this.setDisabled(true);
4641 this.setDisabled(false);
4644 setDisabled : function(state)
4646 if(this.disabled == state){
4650 this.disabled = state;
4653 this.el.addClass('disabled');
4657 this.el.removeClass('disabled');
4662 setActive : function(state)
4664 if(this.active == state){
4668 this.active = state;
4671 this.el.addClass('active');
4675 this.el.removeClass('active');
4680 isActive: function ()
4685 setBadge : function(str)
4691 this.badgeEl.dom.innerHTML = str;
4708 * @class Roo.bootstrap.Row
4709 * @extends Roo.bootstrap.Component
4710 * Bootstrap Row class (contains columns...)
4714 * @param {Object} config The config object
4717 Roo.bootstrap.Row = function(config){
4718 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4721 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4723 getAutoCreate : function(){
4742 * @class Roo.bootstrap.Element
4743 * @extends Roo.bootstrap.Component
4744 * Bootstrap Element class
4745 * @cfg {String} html contents of the element
4746 * @cfg {String} tag tag of the element
4747 * @cfg {String} cls class of the element
4748 * @cfg {Boolean} preventDefault (true|false) default false
4749 * @cfg {Boolean} clickable (true|false) default false
4752 * Create a new Element
4753 * @param {Object} config The config object
4756 Roo.bootstrap.Element = function(config){
4757 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4763 * When a element is chick
4764 * @param {Roo.bootstrap.Element} this
4765 * @param {Roo.EventObject} e
4771 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4776 preventDefault: false,
4779 getAutoCreate : function(){
4790 initEvents: function()
4792 Roo.bootstrap.Element.superclass.initEvents.call(this);
4795 this.el.on('click', this.onClick, this);
4800 onClick : function(e)
4802 if(this.preventDefault){
4806 this.fireEvent('click', this, e);
4809 getValue : function()
4811 return this.el.dom.innerHTML;
4814 setValue : function(value)
4816 this.el.dom.innerHTML = value;
4831 * @class Roo.bootstrap.Pagination
4832 * @extends Roo.bootstrap.Component
4833 * Bootstrap Pagination class
4834 * @cfg {String} size xs | sm | md | lg
4835 * @cfg {Boolean} inverse false | true
4838 * Create a new Pagination
4839 * @param {Object} config The config object
4842 Roo.bootstrap.Pagination = function(config){
4843 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4846 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4852 getAutoCreate : function(){
4858 cfg.cls += ' inverse';
4864 cfg.cls += " " + this.cls;
4882 * @class Roo.bootstrap.PaginationItem
4883 * @extends Roo.bootstrap.Component
4884 * Bootstrap PaginationItem class
4885 * @cfg {String} html text
4886 * @cfg {String} href the link
4887 * @cfg {Boolean} preventDefault (true | false) default true
4888 * @cfg {Boolean} active (true | false) default false
4889 * @cfg {Boolean} disabled default false
4893 * Create a new PaginationItem
4894 * @param {Object} config The config object
4898 Roo.bootstrap.PaginationItem = function(config){
4899 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4904 * The raw click event for the entire grid.
4905 * @param {Roo.EventObject} e
4911 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4915 preventDefault: true,
4920 getAutoCreate : function(){
4926 href : this.href ? this.href : '#',
4927 html : this.html ? this.html : ''
4937 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4941 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4947 initEvents: function() {
4949 this.el.on('click', this.onClick, this);
4952 onClick : function(e)
4954 Roo.log('PaginationItem on click ');
4955 if(this.preventDefault){
4963 this.fireEvent('click', this, e);
4979 * @class Roo.bootstrap.Slider
4980 * @extends Roo.bootstrap.Component
4981 * Bootstrap Slider class
4984 * Create a new Slider
4985 * @param {Object} config The config object
4988 Roo.bootstrap.Slider = function(config){
4989 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4992 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4994 getAutoCreate : function(){
4998 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5002 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5014 * Ext JS Library 1.1.1
5015 * Copyright(c) 2006-2007, Ext JS, LLC.
5017 * Originally Released Under LGPL - original licence link has changed is not relivant.
5020 * <script type="text/javascript">
5025 * @class Roo.grid.ColumnModel
5026 * @extends Roo.util.Observable
5027 * This is the default implementation of a ColumnModel used by the Grid. It defines
5028 * the columns in the grid.
5031 var colModel = new Roo.grid.ColumnModel([
5032 {header: "Ticker", width: 60, sortable: true, locked: true},
5033 {header: "Company Name", width: 150, sortable: true},
5034 {header: "Market Cap.", width: 100, sortable: true},
5035 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5036 {header: "Employees", width: 100, sortable: true, resizable: false}
5041 * The config options listed for this class are options which may appear in each
5042 * individual column definition.
5043 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5045 * @param {Object} config An Array of column config objects. See this class's
5046 * config objects for details.
5048 Roo.grid.ColumnModel = function(config){
5050 * The config passed into the constructor
5052 this.config = config;
5055 // if no id, create one
5056 // if the column does not have a dataIndex mapping,
5057 // map it to the order it is in the config
5058 for(var i = 0, len = config.length; i < len; i++){
5060 if(typeof c.dataIndex == "undefined"){
5063 if(typeof c.renderer == "string"){
5064 c.renderer = Roo.util.Format[c.renderer];
5066 if(typeof c.id == "undefined"){
5069 if(c.editor && c.editor.xtype){
5070 c.editor = Roo.factory(c.editor, Roo.grid);
5072 if(c.editor && c.editor.isFormField){
5073 c.editor = new Roo.grid.GridEditor(c.editor);
5075 this.lookup[c.id] = c;
5079 * The width of columns which have no width specified (defaults to 100)
5082 this.defaultWidth = 100;
5085 * Default sortable of columns which have no sortable specified (defaults to false)
5088 this.defaultSortable = false;
5092 * @event widthchange
5093 * Fires when the width of a column changes.
5094 * @param {ColumnModel} this
5095 * @param {Number} columnIndex The column index
5096 * @param {Number} newWidth The new width
5098 "widthchange": true,
5100 * @event headerchange
5101 * Fires when the text of a header changes.
5102 * @param {ColumnModel} this
5103 * @param {Number} columnIndex The column index
5104 * @param {Number} newText The new header text
5106 "headerchange": true,
5108 * @event hiddenchange
5109 * Fires when a column is hidden or "unhidden".
5110 * @param {ColumnModel} this
5111 * @param {Number} columnIndex The column index
5112 * @param {Boolean} hidden true if hidden, false otherwise
5114 "hiddenchange": true,
5116 * @event columnmoved
5117 * Fires when a column is moved.
5118 * @param {ColumnModel} this
5119 * @param {Number} oldIndex
5120 * @param {Number} newIndex
5122 "columnmoved" : true,
5124 * @event columlockchange
5125 * Fires when a column's locked state is changed
5126 * @param {ColumnModel} this
5127 * @param {Number} colIndex
5128 * @param {Boolean} locked true if locked
5130 "columnlockchange" : true
5132 Roo.grid.ColumnModel.superclass.constructor.call(this);
5134 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5136 * @cfg {String} header The header text to display in the Grid view.
5139 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5140 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5141 * specified, the column's index is used as an index into the Record's data Array.
5144 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5145 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5148 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5149 * Defaults to the value of the {@link #defaultSortable} property.
5150 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5153 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5156 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5159 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5162 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5165 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5166 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5167 * default renderer uses the raw data value. If an object is returned (bootstrap only)
5168 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5171 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5174 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5177 * @cfg {String} cursor (Optional)
5180 * @cfg {String} tooltip (Optional)
5183 * @cfg {Number} xs (Optional)
5186 * @cfg {Number} sm (Optional)
5189 * @cfg {Number} md (Optional)
5192 * @cfg {Number} lg (Optional)
5195 * Returns the id of the column at the specified index.
5196 * @param {Number} index The column index
5197 * @return {String} the id
5199 getColumnId : function(index){
5200 return this.config[index].id;
5204 * Returns the column for a specified id.
5205 * @param {String} id The column id
5206 * @return {Object} the column
5208 getColumnById : function(id){
5209 return this.lookup[id];
5214 * Returns the column for a specified dataIndex.
5215 * @param {String} dataIndex The column dataIndex
5216 * @return {Object|Boolean} the column or false if not found
5218 getColumnByDataIndex: function(dataIndex){
5219 var index = this.findColumnIndex(dataIndex);
5220 return index > -1 ? this.config[index] : false;
5224 * Returns the index for a specified column id.
5225 * @param {String} id The column id
5226 * @return {Number} the index, or -1 if not found
5228 getIndexById : function(id){
5229 for(var i = 0, len = this.config.length; i < len; i++){
5230 if(this.config[i].id == id){
5238 * Returns the index for a specified column dataIndex.
5239 * @param {String} dataIndex The column dataIndex
5240 * @return {Number} the index, or -1 if not found
5243 findColumnIndex : function(dataIndex){
5244 for(var i = 0, len = this.config.length; i < len; i++){
5245 if(this.config[i].dataIndex == dataIndex){
5253 moveColumn : function(oldIndex, newIndex){
5254 var c = this.config[oldIndex];
5255 this.config.splice(oldIndex, 1);
5256 this.config.splice(newIndex, 0, c);
5257 this.dataMap = null;
5258 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5261 isLocked : function(colIndex){
5262 return this.config[colIndex].locked === true;
5265 setLocked : function(colIndex, value, suppressEvent){
5266 if(this.isLocked(colIndex) == value){
5269 this.config[colIndex].locked = value;
5271 this.fireEvent("columnlockchange", this, colIndex, value);
5275 getTotalLockedWidth : function(){
5277 for(var i = 0; i < this.config.length; i++){
5278 if(this.isLocked(i) && !this.isHidden(i)){
5279 this.totalWidth += this.getColumnWidth(i);
5285 getLockedCount : function(){
5286 for(var i = 0, len = this.config.length; i < len; i++){
5287 if(!this.isLocked(i)){
5292 return this.config.length;
5296 * Returns the number of columns.
5299 getColumnCount : function(visibleOnly){
5300 if(visibleOnly === true){
5302 for(var i = 0, len = this.config.length; i < len; i++){
5303 if(!this.isHidden(i)){
5309 return this.config.length;
5313 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5314 * @param {Function} fn
5315 * @param {Object} scope (optional)
5316 * @return {Array} result
5318 getColumnsBy : function(fn, scope){
5320 for(var i = 0, len = this.config.length; i < len; i++){
5321 var c = this.config[i];
5322 if(fn.call(scope||this, c, i) === true){
5330 * Returns true if the specified column is sortable.
5331 * @param {Number} col The column index
5334 isSortable : function(col){
5335 if(typeof this.config[col].sortable == "undefined"){
5336 return this.defaultSortable;
5338 return this.config[col].sortable;
5342 * Returns the rendering (formatting) function defined for the column.
5343 * @param {Number} col The column index.
5344 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5346 getRenderer : function(col){
5347 if(!this.config[col].renderer){
5348 return Roo.grid.ColumnModel.defaultRenderer;
5350 return this.config[col].renderer;
5354 * Sets the rendering (formatting) function for a column.
5355 * @param {Number} col The column index
5356 * @param {Function} fn The function to use to process the cell's raw data
5357 * to return HTML markup for the grid view. The render function is called with
5358 * the following parameters:<ul>
5359 * <li>Data value.</li>
5360 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5361 * <li>css A CSS style string to apply to the table cell.</li>
5362 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5363 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5364 * <li>Row index</li>
5365 * <li>Column index</li>
5366 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5368 setRenderer : function(col, fn){
5369 this.config[col].renderer = fn;
5373 * Returns the width for the specified column.
5374 * @param {Number} col The column index
5377 getColumnWidth : function(col){
5378 return this.config[col].width * 1 || this.defaultWidth;
5382 * Sets the width for a column.
5383 * @param {Number} col The column index
5384 * @param {Number} width The new width
5386 setColumnWidth : function(col, width, suppressEvent){
5387 this.config[col].width = width;
5388 this.totalWidth = null;
5390 this.fireEvent("widthchange", this, col, width);
5395 * Returns the total width of all columns.
5396 * @param {Boolean} includeHidden True to include hidden column widths
5399 getTotalWidth : function(includeHidden){
5400 if(!this.totalWidth){
5401 this.totalWidth = 0;
5402 for(var i = 0, len = this.config.length; i < len; i++){
5403 if(includeHidden || !this.isHidden(i)){
5404 this.totalWidth += this.getColumnWidth(i);
5408 return this.totalWidth;
5412 * Returns the header for the specified column.
5413 * @param {Number} col The column index
5416 getColumnHeader : function(col){
5417 return this.config[col].header;
5421 * Sets the header for a column.
5422 * @param {Number} col The column index
5423 * @param {String} header The new header
5425 setColumnHeader : function(col, header){
5426 this.config[col].header = header;
5427 this.fireEvent("headerchange", this, col, header);
5431 * Returns the tooltip for the specified column.
5432 * @param {Number} col The column index
5435 getColumnTooltip : function(col){
5436 return this.config[col].tooltip;
5439 * Sets the tooltip for a column.
5440 * @param {Number} col The column index
5441 * @param {String} tooltip The new tooltip
5443 setColumnTooltip : function(col, tooltip){
5444 this.config[col].tooltip = tooltip;
5448 * Returns the dataIndex for the specified column.
5449 * @param {Number} col The column index
5452 getDataIndex : function(col){
5453 return this.config[col].dataIndex;
5457 * Sets the dataIndex for a column.
5458 * @param {Number} col The column index
5459 * @param {Number} dataIndex The new dataIndex
5461 setDataIndex : function(col, dataIndex){
5462 this.config[col].dataIndex = dataIndex;
5468 * Returns true if the cell is editable.
5469 * @param {Number} colIndex The column index
5470 * @param {Number} rowIndex The row index - this is nto actually used..?
5473 isCellEditable : function(colIndex, rowIndex){
5474 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5478 * Returns the editor defined for the cell/column.
5479 * return false or null to disable editing.
5480 * @param {Number} colIndex The column index
5481 * @param {Number} rowIndex The row index
5484 getCellEditor : function(colIndex, rowIndex){
5485 return this.config[colIndex].editor;
5489 * Sets if a column is editable.
5490 * @param {Number} col The column index
5491 * @param {Boolean} editable True if the column is editable
5493 setEditable : function(col, editable){
5494 this.config[col].editable = editable;
5499 * Returns true if the column is hidden.
5500 * @param {Number} colIndex The column index
5503 isHidden : function(colIndex){
5504 return this.config[colIndex].hidden;
5509 * Returns true if the column width cannot be changed
5511 isFixed : function(colIndex){
5512 return this.config[colIndex].fixed;
5516 * Returns true if the column can be resized
5519 isResizable : function(colIndex){
5520 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5523 * Sets if a column is hidden.
5524 * @param {Number} colIndex The column index
5525 * @param {Boolean} hidden True if the column is hidden
5527 setHidden : function(colIndex, hidden){
5528 this.config[colIndex].hidden = hidden;
5529 this.totalWidth = null;
5530 this.fireEvent("hiddenchange", this, colIndex, hidden);
5534 * Sets the editor for a column.
5535 * @param {Number} col The column index
5536 * @param {Object} editor The editor object
5538 setEditor : function(col, editor){
5539 this.config[col].editor = editor;
5543 Roo.grid.ColumnModel.defaultRenderer = function(value){
5544 if(typeof value == "string" && value.length < 1){
5550 // Alias for backwards compatibility
5551 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5554 * Ext JS Library 1.1.1
5555 * Copyright(c) 2006-2007, Ext JS, LLC.
5557 * Originally Released Under LGPL - original licence link has changed is not relivant.
5560 * <script type="text/javascript">
5564 * @class Roo.LoadMask
5565 * A simple utility class for generically masking elements while loading data. If the element being masked has
5566 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5567 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5568 * element's UpdateManager load indicator and will be destroyed after the initial load.
5570 * Create a new LoadMask
5571 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5572 * @param {Object} config The config object
5574 Roo.LoadMask = function(el, config){
5575 this.el = Roo.get(el);
5576 Roo.apply(this, config);
5578 this.store.on('beforeload', this.onBeforeLoad, this);
5579 this.store.on('load', this.onLoad, this);
5580 this.store.on('loadexception', this.onLoadException, this);
5581 this.removeMask = false;
5583 var um = this.el.getUpdateManager();
5584 um.showLoadIndicator = false; // disable the default indicator
5585 um.on('beforeupdate', this.onBeforeLoad, this);
5586 um.on('update', this.onLoad, this);
5587 um.on('failure', this.onLoad, this);
5588 this.removeMask = true;
5592 Roo.LoadMask.prototype = {
5594 * @cfg {Boolean} removeMask
5595 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5596 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5600 * The text to display in a centered loading message box (defaults to 'Loading...')
5604 * @cfg {String} msgCls
5605 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5607 msgCls : 'x-mask-loading',
5610 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5616 * Disables the mask to prevent it from being displayed
5618 disable : function(){
5619 this.disabled = true;
5623 * Enables the mask so that it can be displayed
5625 enable : function(){
5626 this.disabled = false;
5629 onLoadException : function()
5633 if (typeof(arguments[3]) != 'undefined') {
5634 Roo.MessageBox.alert("Error loading",arguments[3]);
5638 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5639 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5648 this.el.unmask(this.removeMask);
5653 this.el.unmask(this.removeMask);
5657 onBeforeLoad : function(){
5659 this.el.mask(this.msg, this.msgCls);
5664 destroy : function(){
5666 this.store.un('beforeload', this.onBeforeLoad, this);
5667 this.store.un('load', this.onLoad, this);
5668 this.store.un('loadexception', this.onLoadException, this);
5670 var um = this.el.getUpdateManager();
5671 um.un('beforeupdate', this.onBeforeLoad, this);
5672 um.un('update', this.onLoad, this);
5673 um.un('failure', this.onLoad, this);
5684 * @class Roo.bootstrap.Table
5685 * @extends Roo.bootstrap.Component
5686 * Bootstrap Table class
5687 * @cfg {String} cls table class
5688 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5689 * @cfg {String} bgcolor Specifies the background color for a table
5690 * @cfg {Number} border Specifies whether the table cells should have borders or not
5691 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5692 * @cfg {Number} cellspacing Specifies the space between cells
5693 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5694 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5695 * @cfg {String} sortable Specifies that the table should be sortable
5696 * @cfg {String} summary Specifies a summary of the content of a table
5697 * @cfg {Number} width Specifies the width of a table
5698 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5700 * @cfg {boolean} striped Should the rows be alternative striped
5701 * @cfg {boolean} bordered Add borders to the table
5702 * @cfg {boolean} hover Add hover highlighting
5703 * @cfg {boolean} condensed Format condensed
5704 * @cfg {boolean} responsive Format condensed
5705 * @cfg {Boolean} loadMask (true|false) default false
5706 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5707 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5708 * @cfg {Boolean} rowSelection (true|false) default false
5709 * @cfg {Boolean} cellSelection (true|false) default false
5710 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5711 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5715 * Create a new Table
5716 * @param {Object} config The config object
5719 Roo.bootstrap.Table = function(config){
5720 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5725 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5726 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5727 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5728 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5730 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5732 this.sm.grid = this;
5733 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5734 this.sm = this.selModel;
5735 this.sm.xmodule = this.xmodule || false;
5738 if (this.cm && typeof(this.cm.config) == 'undefined') {
5739 this.colModel = new Roo.grid.ColumnModel(this.cm);
5740 this.cm = this.colModel;
5741 this.cm.xmodule = this.xmodule || false;
5744 this.store= Roo.factory(this.store, Roo.data);
5745 this.ds = this.store;
5746 this.ds.xmodule = this.xmodule || false;
5749 if (this.footer && this.store) {
5750 this.footer.dataSource = this.ds;
5751 this.footer = Roo.factory(this.footer);
5758 * Fires when a cell is clicked
5759 * @param {Roo.bootstrap.Table} this
5760 * @param {Roo.Element} el
5761 * @param {Number} rowIndex
5762 * @param {Number} columnIndex
5763 * @param {Roo.EventObject} e
5767 * @event celldblclick
5768 * Fires when a cell is double clicked
5769 * @param {Roo.bootstrap.Table} this
5770 * @param {Roo.Element} el
5771 * @param {Number} rowIndex
5772 * @param {Number} columnIndex
5773 * @param {Roo.EventObject} e
5775 "celldblclick" : true,
5778 * Fires when a row is clicked
5779 * @param {Roo.bootstrap.Table} this
5780 * @param {Roo.Element} el
5781 * @param {Number} rowIndex
5782 * @param {Roo.EventObject} e
5786 * @event rowdblclick
5787 * Fires when a row is double clicked
5788 * @param {Roo.bootstrap.Table} this
5789 * @param {Roo.Element} el
5790 * @param {Number} rowIndex
5791 * @param {Roo.EventObject} e
5793 "rowdblclick" : true,
5796 * Fires when a mouseover occur
5797 * @param {Roo.bootstrap.Table} this
5798 * @param {Roo.Element} el
5799 * @param {Number} rowIndex
5800 * @param {Number} columnIndex
5801 * @param {Roo.EventObject} e
5806 * Fires when a mouseout occur
5807 * @param {Roo.bootstrap.Table} this
5808 * @param {Roo.Element} el
5809 * @param {Number} rowIndex
5810 * @param {Number} columnIndex
5811 * @param {Roo.EventObject} e
5816 * Fires when a row is rendered, so you can change add a style to it.
5817 * @param {Roo.bootstrap.Table} this
5818 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5822 * @event rowsrendered
5823 * Fires when all the rows have been rendered
5824 * @param {Roo.bootstrap.Table} this
5826 'rowsrendered' : true,
5828 * @event contextmenu
5829 * The raw contextmenu event for the entire grid.
5830 * @param {Roo.EventObject} e
5832 "contextmenu" : true,
5834 * @event rowcontextmenu
5835 * Fires when a row is right clicked
5836 * @param {Roo.bootstrap.Table} this
5837 * @param {Number} rowIndex
5838 * @param {Roo.EventObject} e
5840 "rowcontextmenu" : true,
5842 * @event cellcontextmenu
5843 * Fires when a cell is right clicked
5844 * @param {Roo.bootstrap.Table} this
5845 * @param {Number} rowIndex
5846 * @param {Number} cellIndex
5847 * @param {Roo.EventObject} e
5849 "cellcontextmenu" : true,
5851 * @event headercontextmenu
5852 * Fires when a header is right clicked
5853 * @param {Roo.bootstrap.Table} this
5854 * @param {Number} columnIndex
5855 * @param {Roo.EventObject} e
5857 "headercontextmenu" : true
5861 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5887 rowSelection : false,
5888 cellSelection : false,
5891 // Roo.Element - the tbody
5893 // Roo.Element - thead element
5896 container: false, // used by gridpanel...
5898 getAutoCreate : function()
5900 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5907 if (this.scrollBody) {
5908 cfg.cls += ' table-body-fixed';
5911 cfg.cls += ' table-striped';
5915 cfg.cls += ' table-hover';
5917 if (this.bordered) {
5918 cfg.cls += ' table-bordered';
5920 if (this.condensed) {
5921 cfg.cls += ' table-condensed';
5923 if (this.responsive) {
5924 cfg.cls += ' table-responsive';
5928 cfg.cls+= ' ' +this.cls;
5931 // this lot should be simplifed...
5934 cfg.align=this.align;
5937 cfg.bgcolor=this.bgcolor;
5940 cfg.border=this.border;
5942 if (this.cellpadding) {
5943 cfg.cellpadding=this.cellpadding;
5945 if (this.cellspacing) {
5946 cfg.cellspacing=this.cellspacing;
5949 cfg.frame=this.frame;
5952 cfg.rules=this.rules;
5954 if (this.sortable) {
5955 cfg.sortable=this.sortable;
5958 cfg.summary=this.summary;
5961 cfg.width=this.width;
5964 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5967 if(this.store || this.cm){
5968 if(this.headerShow){
5969 cfg.cn.push(this.renderHeader());
5972 cfg.cn.push(this.renderBody());
5974 if(this.footerShow){
5975 cfg.cn.push(this.renderFooter());
5977 // where does this come from?
5978 //cfg.cls+= ' TableGrid';
5981 return { cn : [ cfg ] };
5984 initEvents : function()
5986 if(!this.store || !this.cm){
5989 if (this.selModel) {
5990 this.selModel.initEvents();
5994 //Roo.log('initEvents with ds!!!!');
5996 this.mainBody = this.el.select('tbody', true).first();
5997 this.mainHead = this.el.select('thead', true).first();
6004 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6005 e.on('click', _this.sort, _this);
6008 this.el.on("click", this.onClick, this);
6009 this.el.on("dblclick", this.onDblClick, this);
6011 // why is this done????? = it breaks dialogs??
6012 //this.parent().el.setStyle('position', 'relative');
6016 this.footer.parentId = this.id;
6017 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6020 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6022 this.store.on('load', this.onLoad, this);
6023 this.store.on('beforeload', this.onBeforeLoad, this);
6024 this.store.on('update', this.onUpdate, this);
6025 this.store.on('add', this.onAdd, this);
6026 this.store.on("clear", this.clear, this);
6028 this.el.on("contextmenu", this.onContextMenu, this);
6030 this.mainBody.on('scroll', this.onBodyScroll, this);
6035 onContextMenu : function(e, t)
6037 this.processEvent("contextmenu", e);
6040 processEvent : function(name, e)
6042 if (name != 'touchstart' ) {
6043 this.fireEvent(name, e);
6046 var t = e.getTarget();
6048 var cell = Roo.get(t);
6054 if(cell.findParent('tfoot', false, true)){
6058 if(cell.findParent('thead', false, true)){
6060 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6061 cell = Roo.get(t).findParent('th', false, true);
6063 Roo.log("failed to find th in thead?");
6064 Roo.log(e.getTarget());
6069 var cellIndex = cell.dom.cellIndex;
6071 var ename = name == 'touchstart' ? 'click' : name;
6072 this.fireEvent("header" + ename, this, cellIndex, e);
6077 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6078 cell = Roo.get(t).findParent('td', false, true);
6080 Roo.log("failed to find th in tbody?");
6081 Roo.log(e.getTarget());
6086 var row = cell.findParent('tr', false, true);
6087 var cellIndex = cell.dom.cellIndex;
6088 var rowIndex = row.dom.rowIndex - 1;
6092 this.fireEvent("row" + name, this, rowIndex, e);
6096 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6102 onMouseover : function(e, el)
6104 var cell = Roo.get(el);
6110 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6111 cell = cell.findParent('td', false, true);
6114 var row = cell.findParent('tr', false, true);
6115 var cellIndex = cell.dom.cellIndex;
6116 var rowIndex = row.dom.rowIndex - 1; // start from 0
6118 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6122 onMouseout : function(e, el)
6124 var cell = Roo.get(el);
6130 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6131 cell = cell.findParent('td', false, true);
6134 var row = cell.findParent('tr', false, true);
6135 var cellIndex = cell.dom.cellIndex;
6136 var rowIndex = row.dom.rowIndex - 1; // start from 0
6138 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6142 onClick : function(e, el)
6144 var cell = Roo.get(el);
6146 if(!cell || (!this.cellSelection && !this.rowSelection)){
6150 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6151 cell = cell.findParent('td', false, true);
6154 if(!cell || typeof(cell) == 'undefined'){
6158 var row = cell.findParent('tr', false, true);
6160 if(!row || typeof(row) == 'undefined'){
6164 var cellIndex = cell.dom.cellIndex;
6165 var rowIndex = this.getRowIndex(row);
6167 // why??? - should these not be based on SelectionModel?
6168 if(this.cellSelection){
6169 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6172 if(this.rowSelection){
6173 this.fireEvent('rowclick', this, row, rowIndex, e);
6179 onDblClick : function(e,el)
6181 var cell = Roo.get(el);
6183 if(!cell || (!this.cellSelection && !this.rowSelection)){
6187 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6188 cell = cell.findParent('td', false, true);
6191 if(!cell || typeof(cell) == 'undefined'){
6195 var row = cell.findParent('tr', false, true);
6197 if(!row || typeof(row) == 'undefined'){
6201 var cellIndex = cell.dom.cellIndex;
6202 var rowIndex = this.getRowIndex(row);
6204 if(this.cellSelection){
6205 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6208 if(this.rowSelection){
6209 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6213 sort : function(e,el)
6215 var col = Roo.get(el);
6217 if(!col.hasClass('sortable')){
6221 var sort = col.attr('sort');
6224 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6228 this.store.sortInfo = {field : sort, direction : dir};
6231 Roo.log("calling footer first");
6232 this.footer.onClick('first');
6235 this.store.load({ params : { start : 0 } });
6239 renderHeader : function()
6247 this.totalWidth = 0;
6249 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6251 var config = cm.config[i];
6256 html: cm.getColumnHeader(i)
6261 if(typeof(config.sortable) != 'undefined' && config.sortable){
6263 c.html = '<i class="glyphicon"></i>' + c.html;
6266 if(typeof(config.lgHeader) != 'undefined'){
6267 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6270 if(typeof(config.mdHeader) != 'undefined'){
6271 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6274 if(typeof(config.smHeader) != 'undefined'){
6275 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6278 if(typeof(config.xsHeader) != 'undefined'){
6279 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6286 if(typeof(config.tooltip) != 'undefined'){
6287 c.tooltip = config.tooltip;
6290 if(typeof(config.colspan) != 'undefined'){
6291 c.colspan = config.colspan;
6294 if(typeof(config.hidden) != 'undefined' && config.hidden){
6295 c.style += ' display:none;';
6298 if(typeof(config.dataIndex) != 'undefined'){
6299 c.sort = config.dataIndex;
6304 if(typeof(config.align) != 'undefined' && config.align.length){
6305 c.style += ' text-align:' + config.align + ';';
6308 if(typeof(config.width) != 'undefined'){
6309 c.style += ' width:' + config.width + 'px;';
6310 this.totalWidth += config.width;
6312 this.totalWidth += 100; // assume minimum of 100 per column?
6315 if(typeof(config.cls) != 'undefined'){
6316 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6319 ['xs','sm','md','lg'].map(function(size){
6321 if(typeof(config[size]) == 'undefined'){
6325 if (!config[size]) { // 0 = hidden
6326 c.cls += ' hidden-' + size;
6330 c.cls += ' col-' + size + '-' + config[size];
6340 renderBody : function()
6350 colspan : this.cm.getColumnCount()
6360 renderFooter : function()
6370 colspan : this.cm.getColumnCount()
6384 // Roo.log('ds onload');
6389 var ds = this.store;
6391 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6392 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6393 if (_this.store.sortInfo) {
6395 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6396 e.select('i', true).addClass(['glyphicon-arrow-up']);
6399 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6400 e.select('i', true).addClass(['glyphicon-arrow-down']);
6405 var tbody = this.mainBody;
6407 if(ds.getCount() > 0){
6408 ds.data.each(function(d,rowIndex){
6409 var row = this.renderRow(cm, ds, rowIndex);
6411 tbody.createChild(row);
6415 if(row.cellObjects.length){
6416 Roo.each(row.cellObjects, function(r){
6417 _this.renderCellObject(r);
6424 Roo.each(this.el.select('tbody td', true).elements, function(e){
6425 e.on('mouseover', _this.onMouseover, _this);
6428 Roo.each(this.el.select('tbody td', true).elements, function(e){
6429 e.on('mouseout', _this.onMouseout, _this);
6431 this.fireEvent('rowsrendered', this);
6432 //if(this.loadMask){
6433 // this.maskEl.hide();
6440 onUpdate : function(ds,record)
6442 this.refreshRow(record);
6446 onRemove : function(ds, record, index, isUpdate){
6447 if(isUpdate !== true){
6448 this.fireEvent("beforerowremoved", this, index, record);
6450 var bt = this.mainBody.dom;
6452 var rows = this.el.select('tbody > tr', true).elements;
6454 if(typeof(rows[index]) != 'undefined'){
6455 bt.removeChild(rows[index].dom);
6458 // if(bt.rows[index]){
6459 // bt.removeChild(bt.rows[index]);
6462 if(isUpdate !== true){
6463 //this.stripeRows(index);
6464 //this.syncRowHeights(index, index);
6466 this.fireEvent("rowremoved", this, index, record);
6470 onAdd : function(ds, records, rowIndex)
6472 //Roo.log('on Add called');
6473 // - note this does not handle multiple adding very well..
6474 var bt = this.mainBody.dom;
6475 for (var i =0 ; i < records.length;i++) {
6476 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6477 //Roo.log(records[i]);
6478 //Roo.log(this.store.getAt(rowIndex+i));
6479 this.insertRow(this.store, rowIndex + i, false);
6486 refreshRow : function(record){
6487 var ds = this.store, index;
6488 if(typeof record == 'number'){
6490 record = ds.getAt(index);
6492 index = ds.indexOf(record);
6494 this.insertRow(ds, index, true);
6496 this.onRemove(ds, record, index+1, true);
6498 //this.syncRowHeights(index, index);
6500 this.fireEvent("rowupdated", this, index, record);
6503 insertRow : function(dm, rowIndex, isUpdate){
6506 this.fireEvent("beforerowsinserted", this, rowIndex);
6508 //var s = this.getScrollState();
6509 var row = this.renderRow(this.cm, this.store, rowIndex);
6510 // insert before rowIndex..
6511 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6515 if(row.cellObjects.length){
6516 Roo.each(row.cellObjects, function(r){
6517 _this.renderCellObject(r);
6522 this.fireEvent("rowsinserted", this, rowIndex);
6523 //this.syncRowHeights(firstRow, lastRow);
6524 //this.stripeRows(firstRow);
6531 getRowDom : function(rowIndex)
6533 var rows = this.el.select('tbody > tr', true).elements;
6535 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6538 // returns the object tree for a tr..
6541 renderRow : function(cm, ds, rowIndex)
6544 var d = ds.getAt(rowIndex);
6551 var cellObjects = [];
6553 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6554 var config = cm.config[i];
6556 var renderer = cm.getRenderer(i);
6560 if(typeof(renderer) !== 'undefined'){
6561 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6563 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6564 // and are rendered into the cells after the row is rendered - using the id for the element.
6566 if(typeof(value) === 'object'){
6576 rowIndex : rowIndex,
6581 this.fireEvent('rowclass', this, rowcfg);
6585 cls : rowcfg.rowClass,
6587 html: (typeof(value) === 'object') ? '' : value
6594 if(typeof(config.colspan) != 'undefined'){
6595 td.colspan = config.colspan;
6598 if(typeof(config.hidden) != 'undefined' && config.hidden){
6599 td.style += ' display:none;';
6602 if(typeof(config.align) != 'undefined' && config.align.length){
6603 td.style += ' text-align:' + config.align + ';';
6606 if(typeof(config.width) != 'undefined'){
6607 td.style += ' width:' + config.width + 'px;';
6610 if(typeof(config.cursor) != 'undefined'){
6611 td.style += ' cursor:' + config.cursor + ';';
6614 if(typeof(config.cls) != 'undefined'){
6615 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6618 ['xs','sm','md','lg'].map(function(size){
6620 if(typeof(config[size]) == 'undefined'){
6624 if (!config[size]) { // 0 = hidden
6625 td.cls += ' hidden-' + size;
6629 td.cls += ' col-' + size + '-' + config[size];
6637 row.cellObjects = cellObjects;
6645 onBeforeLoad : function()
6647 //Roo.log('ds onBeforeLoad');
6651 //if(this.loadMask){
6652 // this.maskEl.show();
6660 this.el.select('tbody', true).first().dom.innerHTML = '';
6663 * Show or hide a row.
6664 * @param {Number} rowIndex to show or hide
6665 * @param {Boolean} state hide
6667 setRowVisibility : function(rowIndex, state)
6669 var bt = this.mainBody.dom;
6671 var rows = this.el.select('tbody > tr', true).elements;
6673 if(typeof(rows[rowIndex]) == 'undefined'){
6676 rows[rowIndex].dom.style.display = state ? '' : 'none';
6680 getSelectionModel : function(){
6682 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6684 return this.selModel;
6687 * Render the Roo.bootstrap object from renderder
6689 renderCellObject : function(r)
6693 var t = r.cfg.render(r.container);
6696 Roo.each(r.cfg.cn, function(c){
6698 container: t.getChildContainer(),
6701 _this.renderCellObject(child);
6706 getRowIndex : function(row)
6710 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6721 * Returns the grid's underlying element = used by panel.Grid
6722 * @return {Element} The element
6724 getGridEl : function(){
6728 * Forces a resize - used by panel.Grid
6729 * @return {Element} The element
6731 autoSize : function()
6733 //var ctr = Roo.get(this.container.dom.parentElement);
6734 var ctr = Roo.get(this.el.dom);
6736 var thd = this.getGridEl().select('thead',true).first();
6737 var tbd = this.getGridEl().select('tbody', true).first();
6738 var tfd = this.getGridEl().select('tfoot', true).first();
6740 var cw = ctr.getWidth();
6744 tbd.setSize(ctr.getWidth(),
6745 ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6747 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6750 cw = Math.max(cw, this.totalWidth);
6751 this.getGridEl().select('tr',true).setWidth(cw);
6752 // resize 'expandable coloumn?
6754 return; // we doe not have a view in this design..
6757 onBodyScroll: function()
6760 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6761 this.mainHead.setStyle({
6762 'position' : 'relative',
6763 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6780 * @class Roo.bootstrap.TableCell
6781 * @extends Roo.bootstrap.Component
6782 * Bootstrap TableCell class
6783 * @cfg {String} html cell contain text
6784 * @cfg {String} cls cell class
6785 * @cfg {String} tag cell tag (td|th) default td
6786 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6787 * @cfg {String} align Aligns the content in a cell
6788 * @cfg {String} axis Categorizes cells
6789 * @cfg {String} bgcolor Specifies the background color of a cell
6790 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6791 * @cfg {Number} colspan Specifies the number of columns a cell should span
6792 * @cfg {String} headers Specifies one or more header cells a cell is related to
6793 * @cfg {Number} height Sets the height of a cell
6794 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6795 * @cfg {Number} rowspan Sets the number of rows a cell should span
6796 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6797 * @cfg {String} valign Vertical aligns the content in a cell
6798 * @cfg {Number} width Specifies the width of a cell
6801 * Create a new TableCell
6802 * @param {Object} config The config object
6805 Roo.bootstrap.TableCell = function(config){
6806 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6809 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6829 getAutoCreate : function(){
6830 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6850 cfg.align=this.align
6856 cfg.bgcolor=this.bgcolor
6859 cfg.charoff=this.charoff
6862 cfg.colspan=this.colspan
6865 cfg.headers=this.headers
6868 cfg.height=this.height
6871 cfg.nowrap=this.nowrap
6874 cfg.rowspan=this.rowspan
6877 cfg.scope=this.scope
6880 cfg.valign=this.valign
6883 cfg.width=this.width
6902 * @class Roo.bootstrap.TableRow
6903 * @extends Roo.bootstrap.Component
6904 * Bootstrap TableRow class
6905 * @cfg {String} cls row class
6906 * @cfg {String} align Aligns the content in a table row
6907 * @cfg {String} bgcolor Specifies a background color for a table row
6908 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6909 * @cfg {String} valign Vertical aligns the content in a table row
6912 * Create a new TableRow
6913 * @param {Object} config The config object
6916 Roo.bootstrap.TableRow = function(config){
6917 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6920 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6928 getAutoCreate : function(){
6929 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6939 cfg.align = this.align;
6942 cfg.bgcolor = this.bgcolor;
6945 cfg.charoff = this.charoff;
6948 cfg.valign = this.valign;
6966 * @class Roo.bootstrap.TableBody
6967 * @extends Roo.bootstrap.Component
6968 * Bootstrap TableBody class
6969 * @cfg {String} cls element class
6970 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6971 * @cfg {String} align Aligns the content inside the element
6972 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6973 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6976 * Create a new TableBody
6977 * @param {Object} config The config object
6980 Roo.bootstrap.TableBody = function(config){
6981 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6984 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6992 getAutoCreate : function(){
6993 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7007 cfg.align = this.align;
7010 cfg.charoff = this.charoff;
7013 cfg.valign = this.valign;
7020 // initEvents : function()
7027 // this.store = Roo.factory(this.store, Roo.data);
7028 // this.store.on('load', this.onLoad, this);
7030 // this.store.load();
7034 // onLoad: function ()
7036 // this.fireEvent('load', this);
7046 * Ext JS Library 1.1.1
7047 * Copyright(c) 2006-2007, Ext JS, LLC.
7049 * Originally Released Under LGPL - original licence link has changed is not relivant.
7052 * <script type="text/javascript">
7055 // as we use this in bootstrap.
7056 Roo.namespace('Roo.form');
7058 * @class Roo.form.Action
7059 * Internal Class used to handle form actions
7061 * @param {Roo.form.BasicForm} el The form element or its id
7062 * @param {Object} config Configuration options
7067 // define the action interface
7068 Roo.form.Action = function(form, options){
7070 this.options = options || {};
7073 * Client Validation Failed
7076 Roo.form.Action.CLIENT_INVALID = 'client';
7078 * Server Validation Failed
7081 Roo.form.Action.SERVER_INVALID = 'server';
7083 * Connect to Server Failed
7086 Roo.form.Action.CONNECT_FAILURE = 'connect';
7088 * Reading Data from Server Failed
7091 Roo.form.Action.LOAD_FAILURE = 'load';
7093 Roo.form.Action.prototype = {
7095 failureType : undefined,
7096 response : undefined,
7100 run : function(options){
7105 success : function(response){
7110 handleResponse : function(response){
7114 // default connection failure
7115 failure : function(response){
7117 this.response = response;
7118 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7119 this.form.afterAction(this, false);
7122 processResponse : function(response){
7123 this.response = response;
7124 if(!response.responseText){
7127 this.result = this.handleResponse(response);
7131 // utility functions used internally
7132 getUrl : function(appendParams){
7133 var url = this.options.url || this.form.url || this.form.el.dom.action;
7135 var p = this.getParams();
7137 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7143 getMethod : function(){
7144 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7147 getParams : function(){
7148 var bp = this.form.baseParams;
7149 var p = this.options.params;
7151 if(typeof p == "object"){
7152 p = Roo.urlEncode(Roo.applyIf(p, bp));
7153 }else if(typeof p == 'string' && bp){
7154 p += '&' + Roo.urlEncode(bp);
7157 p = Roo.urlEncode(bp);
7162 createCallback : function(){
7164 success: this.success,
7165 failure: this.failure,
7167 timeout: (this.form.timeout*1000),
7168 upload: this.form.fileUpload ? this.success : undefined
7173 Roo.form.Action.Submit = function(form, options){
7174 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7177 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7180 haveProgress : false,
7181 uploadComplete : false,
7183 // uploadProgress indicator.
7184 uploadProgress : function()
7186 if (!this.form.progressUrl) {
7190 if (!this.haveProgress) {
7191 Roo.MessageBox.progress("Uploading", "Uploading");
7193 if (this.uploadComplete) {
7194 Roo.MessageBox.hide();
7198 this.haveProgress = true;
7200 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7202 var c = new Roo.data.Connection();
7204 url : this.form.progressUrl,
7209 success : function(req){
7210 //console.log(data);
7214 rdata = Roo.decode(req.responseText)
7216 Roo.log("Invalid data from server..");
7220 if (!rdata || !rdata.success) {
7222 Roo.MessageBox.alert(Roo.encode(rdata));
7225 var data = rdata.data;
7227 if (this.uploadComplete) {
7228 Roo.MessageBox.hide();
7233 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7234 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7237 this.uploadProgress.defer(2000,this);
7240 failure: function(data) {
7241 Roo.log('progress url failed ');
7252 // run get Values on the form, so it syncs any secondary forms.
7253 this.form.getValues();
7255 var o = this.options;
7256 var method = this.getMethod();
7257 var isPost = method == 'POST';
7258 if(o.clientValidation === false || this.form.isValid()){
7260 if (this.form.progressUrl) {
7261 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7262 (new Date() * 1) + '' + Math.random());
7267 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7268 form:this.form.el.dom,
7269 url:this.getUrl(!isPost),
7271 params:isPost ? this.getParams() : null,
7272 isUpload: this.form.fileUpload
7275 this.uploadProgress();
7277 }else if (o.clientValidation !== false){ // client validation failed
7278 this.failureType = Roo.form.Action.CLIENT_INVALID;
7279 this.form.afterAction(this, false);
7283 success : function(response)
7285 this.uploadComplete= true;
7286 if (this.haveProgress) {
7287 Roo.MessageBox.hide();
7291 var result = this.processResponse(response);
7292 if(result === true || result.success){
7293 this.form.afterAction(this, true);
7297 this.form.markInvalid(result.errors);
7298 this.failureType = Roo.form.Action.SERVER_INVALID;
7300 this.form.afterAction(this, false);
7302 failure : function(response)
7304 this.uploadComplete= true;
7305 if (this.haveProgress) {
7306 Roo.MessageBox.hide();
7309 this.response = response;
7310 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7311 this.form.afterAction(this, false);
7314 handleResponse : function(response){
7315 if(this.form.errorReader){
7316 var rs = this.form.errorReader.read(response);
7319 for(var i = 0, len = rs.records.length; i < len; i++) {
7320 var r = rs.records[i];
7324 if(errors.length < 1){
7328 success : rs.success,
7334 ret = Roo.decode(response.responseText);
7338 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7348 Roo.form.Action.Load = function(form, options){
7349 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7350 this.reader = this.form.reader;
7353 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7358 Roo.Ajax.request(Roo.apply(
7359 this.createCallback(), {
7360 method:this.getMethod(),
7361 url:this.getUrl(false),
7362 params:this.getParams()
7366 success : function(response){
7368 var result = this.processResponse(response);
7369 if(result === true || !result.success || !result.data){
7370 this.failureType = Roo.form.Action.LOAD_FAILURE;
7371 this.form.afterAction(this, false);
7374 this.form.clearInvalid();
7375 this.form.setValues(result.data);
7376 this.form.afterAction(this, true);
7379 handleResponse : function(response){
7380 if(this.form.reader){
7381 var rs = this.form.reader.read(response);
7382 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7384 success : rs.success,
7388 return Roo.decode(response.responseText);
7392 Roo.form.Action.ACTION_TYPES = {
7393 'load' : Roo.form.Action.Load,
7394 'submit' : Roo.form.Action.Submit
7403 * @class Roo.bootstrap.Form
7404 * @extends Roo.bootstrap.Component
7405 * Bootstrap Form class
7406 * @cfg {String} method GET | POST (default POST)
7407 * @cfg {String} labelAlign top | left (default top)
7408 * @cfg {String} align left | right - for navbars
7409 * @cfg {Boolean} loadMask load mask when submit (default true)
7414 * @param {Object} config The config object
7418 Roo.bootstrap.Form = function(config){
7419 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7422 * @event clientvalidation
7423 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7424 * @param {Form} this
7425 * @param {Boolean} valid true if the form has passed client-side validation
7427 clientvalidation: true,
7429 * @event beforeaction
7430 * Fires before any action is performed. Return false to cancel the action.
7431 * @param {Form} this
7432 * @param {Action} action The action to be performed
7436 * @event actionfailed
7437 * Fires when an action fails.
7438 * @param {Form} this
7439 * @param {Action} action The action that failed
7441 actionfailed : true,
7443 * @event actioncomplete
7444 * Fires when an action is completed.
7445 * @param {Form} this
7446 * @param {Action} action The action that completed
7448 actioncomplete : true
7453 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7456 * @cfg {String} method
7457 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7462 * The URL to use for form actions if one isn't supplied in the action options.
7465 * @cfg {Boolean} fileUpload
7466 * Set to true if this form is a file upload.
7470 * @cfg {Object} baseParams
7471 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7475 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7479 * @cfg {Sting} align (left|right) for navbar forms
7484 activeAction : null,
7487 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7488 * element by passing it or its id or mask the form itself by passing in true.
7491 waitMsgTarget : false,
7495 getAutoCreate : function(){
7499 method : this.method || 'POST',
7500 id : this.id || Roo.id(),
7503 if (this.parent().xtype.match(/^Nav/)) {
7504 cfg.cls = 'navbar-form navbar-' + this.align;
7508 if (this.labelAlign == 'left' ) {
7509 cfg.cls += ' form-horizontal';
7515 initEvents : function()
7517 this.el.on('submit', this.onSubmit, this);
7518 // this was added as random key presses on the form where triggering form submit.
7519 this.el.on('keypress', function(e) {
7520 if (e.getCharCode() != 13) {
7523 // we might need to allow it for textareas.. and some other items.
7524 // check e.getTarget().
7526 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7530 Roo.log("keypress blocked");
7538 onSubmit : function(e){
7543 * Returns true if client-side validation on the form is successful.
7546 isValid : function(){
7547 var items = this.getItems();
7549 items.each(function(f){
7558 * Returns true if any fields in this form have changed since their original load.
7561 isDirty : function(){
7563 var items = this.getItems();
7564 items.each(function(f){
7574 * Performs a predefined action (submit or load) or custom actions you define on this form.
7575 * @param {String} actionName The name of the action type
7576 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7577 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7578 * accept other config options):
7580 Property Type Description
7581 ---------------- --------------- ----------------------------------------------------------------------------------
7582 url String The url for the action (defaults to the form's url)
7583 method String The form method to use (defaults to the form's method, or POST if not defined)
7584 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7585 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7586 validate the form on the client (defaults to false)
7588 * @return {BasicForm} this
7590 doAction : function(action, options){
7591 if(typeof action == 'string'){
7592 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7594 if(this.fireEvent('beforeaction', this, action) !== false){
7595 this.beforeAction(action);
7596 action.run.defer(100, action);
7602 beforeAction : function(action){
7603 var o = action.options;
7606 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7608 // not really supported yet.. ??
7610 //if(this.waitMsgTarget === true){
7611 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7612 //}else if(this.waitMsgTarget){
7613 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7614 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7616 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7622 afterAction : function(action, success){
7623 this.activeAction = null;
7624 var o = action.options;
7626 //if(this.waitMsgTarget === true){
7628 //}else if(this.waitMsgTarget){
7629 // this.waitMsgTarget.unmask();
7631 // Roo.MessageBox.updateProgress(1);
7632 // Roo.MessageBox.hide();
7639 Roo.callback(o.success, o.scope, [this, action]);
7640 this.fireEvent('actioncomplete', this, action);
7644 // failure condition..
7645 // we have a scenario where updates need confirming.
7646 // eg. if a locking scenario exists..
7647 // we look for { errors : { needs_confirm : true }} in the response.
7649 (typeof(action.result) != 'undefined') &&
7650 (typeof(action.result.errors) != 'undefined') &&
7651 (typeof(action.result.errors.needs_confirm) != 'undefined')
7654 Roo.log("not supported yet");
7657 Roo.MessageBox.confirm(
7658 "Change requires confirmation",
7659 action.result.errorMsg,
7664 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7674 Roo.callback(o.failure, o.scope, [this, action]);
7675 // show an error message if no failed handler is set..
7676 if (!this.hasListener('actionfailed')) {
7677 Roo.log("need to add dialog support");
7679 Roo.MessageBox.alert("Error",
7680 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7681 action.result.errorMsg :
7682 "Saving Failed, please check your entries or try again"
7687 this.fireEvent('actionfailed', this, action);
7692 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7693 * @param {String} id The value to search for
7696 findField : function(id){
7697 var items = this.getItems();
7698 var field = items.get(id);
7700 items.each(function(f){
7701 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7708 return field || null;
7711 * Mark fields in this form invalid in bulk.
7712 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7713 * @return {BasicForm} this
7715 markInvalid : function(errors){
7716 if(errors instanceof Array){
7717 for(var i = 0, len = errors.length; i < len; i++){
7718 var fieldError = errors[i];
7719 var f = this.findField(fieldError.id);
7721 f.markInvalid(fieldError.msg);
7727 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7728 field.markInvalid(errors[id]);
7732 //Roo.each(this.childForms || [], function (f) {
7733 // f.markInvalid(errors);
7740 * Set values for fields in this form in bulk.
7741 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7742 * @return {BasicForm} this
7744 setValues : function(values){
7745 if(values instanceof Array){ // array of objects
7746 for(var i = 0, len = values.length; i < len; i++){
7748 var f = this.findField(v.id);
7750 f.setValue(v.value);
7751 if(this.trackResetOnLoad){
7752 f.originalValue = f.getValue();
7756 }else{ // object hash
7759 if(typeof values[id] != 'function' && (field = this.findField(id))){
7761 if (field.setFromData &&
7763 field.displayField &&
7764 // combos' with local stores can
7765 // be queried via setValue()
7766 // to set their value..
7767 (field.store && !field.store.isLocal)
7771 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7772 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7773 field.setFromData(sd);
7776 field.setValue(values[id]);
7780 if(this.trackResetOnLoad){
7781 field.originalValue = field.getValue();
7787 //Roo.each(this.childForms || [], function (f) {
7788 // f.setValues(values);
7795 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7796 * they are returned as an array.
7797 * @param {Boolean} asString
7800 getValues : function(asString){
7801 //if (this.childForms) {
7802 // copy values from the child forms
7803 // Roo.each(this.childForms, function (f) {
7804 // this.setValues(f.getValues());
7810 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7811 if(asString === true){
7814 return Roo.urlDecode(fs);
7818 * Returns the fields in this form as an object with key/value pairs.
7819 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7822 getFieldValues : function(with_hidden)
7824 var items = this.getItems();
7826 items.each(function(f){
7830 var v = f.getValue();
7831 if (f.inputType =='radio') {
7832 if (typeof(ret[f.getName()]) == 'undefined') {
7833 ret[f.getName()] = ''; // empty..
7836 if (!f.el.dom.checked) {
7844 // not sure if this supported any more..
7845 if ((typeof(v) == 'object') && f.getRawValue) {
7846 v = f.getRawValue() ; // dates..
7848 // combo boxes where name != hiddenName...
7849 if (f.name != f.getName()) {
7850 ret[f.name] = f.getRawValue();
7852 ret[f.getName()] = v;
7859 * Clears all invalid messages in this form.
7860 * @return {BasicForm} this
7862 clearInvalid : function(){
7863 var items = this.getItems();
7865 items.each(function(f){
7876 * @return {BasicForm} this
7879 var items = this.getItems();
7880 items.each(function(f){
7884 Roo.each(this.childForms || [], function (f) {
7891 getItems : function()
7893 var r=new Roo.util.MixedCollection(false, function(o){
7894 return o.id || (o.id = Roo.id());
7896 var iter = function(el) {
7903 Roo.each(el.items,function(e) {
7923 * Ext JS Library 1.1.1
7924 * Copyright(c) 2006-2007, Ext JS, LLC.
7926 * Originally Released Under LGPL - original licence link has changed is not relivant.
7929 * <script type="text/javascript">
7932 * @class Roo.form.VTypes
7933 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7936 Roo.form.VTypes = function(){
7937 // closure these in so they are only created once.
7938 var alpha = /^[a-zA-Z_]+$/;
7939 var alphanum = /^[a-zA-Z0-9_]+$/;
7940 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7941 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7943 // All these messages and functions are configurable
7946 * The function used to validate email addresses
7947 * @param {String} value The email address
7949 'email' : function(v){
7950 return email.test(v);
7953 * The error text to display when the email validation function returns false
7956 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7958 * The keystroke filter mask to be applied on email input
7961 'emailMask' : /[a-z0-9_\.\-@]/i,
7964 * The function used to validate URLs
7965 * @param {String} value The URL
7967 'url' : function(v){
7971 * The error text to display when the url validation function returns false
7974 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7977 * The function used to validate alpha values
7978 * @param {String} value The value
7980 'alpha' : function(v){
7981 return alpha.test(v);
7984 * The error text to display when the alpha validation function returns false
7987 'alphaText' : 'This field should only contain letters and _',
7989 * The keystroke filter mask to be applied on alpha input
7992 'alphaMask' : /[a-z_]/i,
7995 * The function used to validate alphanumeric values
7996 * @param {String} value The value
7998 'alphanum' : function(v){
7999 return alphanum.test(v);
8002 * The error text to display when the alphanumeric validation function returns false
8005 'alphanumText' : 'This field should only contain letters, numbers and _',
8007 * The keystroke filter mask to be applied on alphanumeric input
8010 'alphanumMask' : /[a-z0-9_]/i
8020 * @class Roo.bootstrap.Input
8021 * @extends Roo.bootstrap.Component
8022 * Bootstrap Input class
8023 * @cfg {Boolean} disabled is it disabled
8024 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8025 * @cfg {String} name name of the input
8026 * @cfg {string} fieldLabel - the label associated
8027 * @cfg {string} placeholder - placeholder to put in text.
8028 * @cfg {string} before - input group add on before
8029 * @cfg {string} after - input group add on after
8030 * @cfg {string} size - (lg|sm) or leave empty..
8031 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8032 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8033 * @cfg {Number} md colspan out of 12 for computer-sized screens
8034 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8035 * @cfg {string} value default value of the input
8036 * @cfg {Number} labelWidth set the width of label (0-12)
8037 * @cfg {String} labelAlign (top|left)
8038 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8039 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8040 * @cfg {String} indicatorpos (left|right) default left
8042 * @cfg {String} align (left|center|right) Default left
8043 * @cfg {Boolean} forceFeedback (true|false) Default false
8049 * Create a new Input
8050 * @param {Object} config The config object
8053 Roo.bootstrap.Input = function(config){
8054 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8059 * Fires when this field receives input focus.
8060 * @param {Roo.form.Field} this
8065 * Fires when this field loses input focus.
8066 * @param {Roo.form.Field} this
8071 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8072 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8073 * @param {Roo.form.Field} this
8074 * @param {Roo.EventObject} e The event object
8079 * Fires just before the field blurs if the field value has changed.
8080 * @param {Roo.form.Field} this
8081 * @param {Mixed} newValue The new value
8082 * @param {Mixed} oldValue The original value
8087 * Fires after the field has been marked as invalid.
8088 * @param {Roo.form.Field} this
8089 * @param {String} msg The validation message
8094 * Fires after the field has been validated with no errors.
8095 * @param {Roo.form.Field} this
8100 * Fires after the key up
8101 * @param {Roo.form.Field} this
8102 * @param {Roo.EventObject} e The event Object
8108 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8110 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8111 automatic validation (defaults to "keyup").
8113 validationEvent : "keyup",
8115 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8117 validateOnBlur : true,
8119 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8121 validationDelay : 250,
8123 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8125 focusClass : "x-form-focus", // not needed???
8129 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8131 invalidClass : "has-warning",
8134 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8136 validClass : "has-success",
8139 * @cfg {Boolean} hasFeedback (true|false) default true
8144 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8146 invalidFeedbackClass : "glyphicon-warning-sign",
8149 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8151 validFeedbackClass : "glyphicon-ok",
8154 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8156 selectOnFocus : false,
8159 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8163 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8168 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8170 disableKeyFilter : false,
8173 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8177 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8181 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8183 blankText : "This field is required",
8186 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8190 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8192 maxLength : Number.MAX_VALUE,
8194 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8196 minLengthText : "The minimum length for this field is {0}",
8198 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8200 maxLengthText : "The maximum length for this field is {0}",
8204 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8205 * If available, this function will be called only after the basic validators all return true, and will be passed the
8206 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8210 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8211 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8212 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8216 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8220 autocomplete: false,
8239 formatedValue : false,
8240 forceFeedback : false,
8242 indicatorpos : 'left',
8244 parentLabelAlign : function()
8247 while (parent.parent()) {
8248 parent = parent.parent();
8249 if (typeof(parent.labelAlign) !='undefined') {
8250 return parent.labelAlign;
8257 getAutoCreate : function()
8259 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8265 if(this.inputType != 'hidden'){
8266 cfg.cls = 'form-group' //input-group
8272 type : this.inputType,
8274 cls : 'form-control',
8275 placeholder : this.placeholder || '',
8276 autocomplete : this.autocomplete || 'new-password'
8280 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8283 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8284 input.maxLength = this.maxLength;
8287 if (this.disabled) {
8288 input.disabled=true;
8291 if (this.readOnly) {
8292 input.readonly=true;
8296 input.name = this.name;
8300 input.cls += ' input-' + this.size;
8304 ['xs','sm','md','lg'].map(function(size){
8305 if (settings[size]) {
8306 cfg.cls += ' col-' + size + '-' + settings[size];
8310 var inputblock = input;
8314 cls: 'glyphicon form-control-feedback'
8317 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8320 cls : 'has-feedback',
8328 if (this.before || this.after) {
8331 cls : 'input-group',
8335 if (this.before && typeof(this.before) == 'string') {
8337 inputblock.cn.push({
8339 cls : 'roo-input-before input-group-addon',
8343 if (this.before && typeof(this.before) == 'object') {
8344 this.before = Roo.factory(this.before);
8346 inputblock.cn.push({
8348 cls : 'roo-input-before input-group-' +
8349 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8353 inputblock.cn.push(input);
8355 if (this.after && typeof(this.after) == 'string') {
8356 inputblock.cn.push({
8358 cls : 'roo-input-after input-group-addon',
8362 if (this.after && typeof(this.after) == 'object') {
8363 this.after = Roo.factory(this.after);
8365 inputblock.cn.push({
8367 cls : 'roo-input-after input-group-' +
8368 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8372 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8373 inputblock.cls += ' has-feedback';
8374 inputblock.cn.push(feedback);
8378 if (align ==='left' && this.fieldLabel.length) {
8383 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8384 tooltip : 'This field is required'
8389 cls : 'control-label col-sm-' + this.labelWidth,
8390 html : this.fieldLabel
8394 cls : "col-sm-" + (12 - this.labelWidth),
8402 if(this.indicatorpos == 'right'){
8407 cls : 'control-label col-sm-' + this.labelWidth,
8408 html : this.fieldLabel
8413 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8414 tooltip : 'This field is required'
8417 cls : "col-sm-" + (12 - this.labelWidth),
8426 } else if ( this.fieldLabel.length) {
8431 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8432 tooltip : 'This field is required'
8436 //cls : 'input-group-addon',
8437 html : this.fieldLabel
8445 if(this.indicatorpos == 'right'){
8450 //cls : 'input-group-addon',
8451 html : this.fieldLabel
8456 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8457 tooltip : 'This field is required'
8477 if (this.parentType === 'Navbar' && this.parent().bar) {
8478 cfg.cls += ' navbar-form';
8481 if (this.parentType === 'NavGroup') {
8482 cfg.cls += ' navbar-form';
8490 * return the real input element.
8492 inputEl: function ()
8494 return this.el.select('input.form-control',true).first();
8497 tooltipEl : function()
8499 return this.inputEl();
8502 indicatorEl : function()
8504 var indicator = this.el.select('i.roo-required-indicator',true).first();
8514 setDisabled : function(v)
8516 var i = this.inputEl().dom;
8518 i.removeAttribute('disabled');
8522 i.setAttribute('disabled','true');
8524 initEvents : function()
8527 this.inputEl().on("keydown" , this.fireKey, this);
8528 this.inputEl().on("focus", this.onFocus, this);
8529 this.inputEl().on("blur", this.onBlur, this);
8531 this.inputEl().relayEvent('keyup', this);
8533 this.indicator = this.indicatorEl();
8536 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8537 this.indicator.hide();
8540 // reference to original value for reset
8541 this.originalValue = this.getValue();
8542 //Roo.form.TextField.superclass.initEvents.call(this);
8543 if(this.validationEvent == 'keyup'){
8544 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8545 this.inputEl().on('keyup', this.filterValidation, this);
8547 else if(this.validationEvent !== false){
8548 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8551 if(this.selectOnFocus){
8552 this.on("focus", this.preFocus, this);
8555 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8556 this.inputEl().on("keypress", this.filterKeys, this);
8558 this.inputEl().relayEvent('keypress', this);
8561 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8562 this.el.on("click", this.autoSize, this);
8565 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8566 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8569 if (typeof(this.before) == 'object') {
8570 this.before.render(this.el.select('.roo-input-before',true).first());
8572 if (typeof(this.after) == 'object') {
8573 this.after.render(this.el.select('.roo-input-after',true).first());
8578 filterValidation : function(e){
8579 if(!e.isNavKeyPress()){
8580 this.validationTask.delay(this.validationDelay);
8584 * Validates the field value
8585 * @return {Boolean} True if the value is valid, else false
8587 validate : function(){
8588 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8589 if(this.disabled || this.validateValue(this.getRawValue())){
8600 * Validates a value according to the field's validation rules and marks the field as invalid
8601 * if the validation fails
8602 * @param {Mixed} value The value to validate
8603 * @return {Boolean} True if the value is valid, else false
8605 validateValue : function(value){
8606 if(value.length < 1) { // if it's blank
8607 if(this.allowBlank){
8613 if(value.length < this.minLength){
8616 if(value.length > this.maxLength){
8620 var vt = Roo.form.VTypes;
8621 if(!vt[this.vtype](value, this)){
8625 if(typeof this.validator == "function"){
8626 var msg = this.validator(value);
8632 if(this.regex && !this.regex.test(value)){
8642 fireKey : function(e){
8643 //Roo.log('field ' + e.getKey());
8644 if(e.isNavKeyPress()){
8645 this.fireEvent("specialkey", this, e);
8648 focus : function (selectText){
8650 this.inputEl().focus();
8651 if(selectText === true){
8652 this.inputEl().dom.select();
8658 onFocus : function(){
8659 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8660 // this.el.addClass(this.focusClass);
8663 this.hasFocus = true;
8664 this.startValue = this.getValue();
8665 this.fireEvent("focus", this);
8669 beforeBlur : Roo.emptyFn,
8673 onBlur : function(){
8675 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8676 //this.el.removeClass(this.focusClass);
8678 this.hasFocus = false;
8679 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8682 var v = this.getValue();
8683 if(String(v) !== String(this.startValue)){
8684 this.fireEvent('change', this, v, this.startValue);
8686 this.fireEvent("blur", this);
8690 * Resets the current field value to the originally loaded value and clears any validation messages
8693 this.setValue(this.originalValue);
8697 * Returns the name of the field
8698 * @return {Mixed} name The name field
8700 getName: function(){
8704 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8705 * @return {Mixed} value The field value
8707 getValue : function(){
8709 var v = this.inputEl().getValue();
8714 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8715 * @return {Mixed} value The field value
8717 getRawValue : function(){
8718 var v = this.inputEl().getValue();
8724 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8725 * @param {Mixed} value The value to set
8727 setRawValue : function(v){
8728 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8731 selectText : function(start, end){
8732 var v = this.getRawValue();
8734 start = start === undefined ? 0 : start;
8735 end = end === undefined ? v.length : end;
8736 var d = this.inputEl().dom;
8737 if(d.setSelectionRange){
8738 d.setSelectionRange(start, end);
8739 }else if(d.createTextRange){
8740 var range = d.createTextRange();
8741 range.moveStart("character", start);
8742 range.moveEnd("character", v.length-end);
8749 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8750 * @param {Mixed} value The value to set
8752 setValue : function(v){
8755 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8761 processValue : function(value){
8762 if(this.stripCharsRe){
8763 var newValue = value.replace(this.stripCharsRe, '');
8764 if(newValue !== value){
8765 this.setRawValue(newValue);
8772 preFocus : function(){
8774 if(this.selectOnFocus){
8775 this.inputEl().dom.select();
8778 filterKeys : function(e){
8780 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8783 var c = e.getCharCode(), cc = String.fromCharCode(c);
8784 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8787 if(!this.maskRe.test(cc)){
8792 * Clear any invalid styles/messages for this field
8794 clearInvalid : function(){
8796 if(!this.el || this.preventMark){ // not rendered
8801 this.indicator.hide();
8804 this.el.removeClass(this.invalidClass);
8806 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8808 var feedback = this.el.select('.form-control-feedback', true).first();
8811 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8816 this.fireEvent('valid', this);
8820 * Mark this field as valid
8822 markValid : function()
8824 if(!this.el || this.preventMark){ // not rendered
8828 this.el.removeClass([this.invalidClass, this.validClass]);
8830 var feedback = this.el.select('.form-control-feedback', true).first();
8833 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8836 if(this.disabled || this.allowBlank){
8841 this.indicator.hide();
8844 this.el.addClass(this.validClass);
8846 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8848 var feedback = this.el.select('.form-control-feedback', true).first();
8851 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8852 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8857 this.fireEvent('valid', this);
8861 * Mark this field as invalid
8862 * @param {String} msg The validation message
8864 markInvalid : function(msg)
8866 if(!this.el || this.preventMark){ // not rendered
8870 this.el.removeClass([this.invalidClass, this.validClass]);
8872 var feedback = this.el.select('.form-control-feedback', true).first();
8875 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8878 if(this.disabled || this.allowBlank){
8883 this.indicator.show();
8886 this.el.addClass(this.invalidClass);
8888 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8890 var feedback = this.el.select('.form-control-feedback', true).first();
8893 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8895 if(this.getValue().length || this.forceFeedback){
8896 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8903 this.fireEvent('invalid', this, msg);
8906 SafariOnKeyDown : function(event)
8908 // this is a workaround for a password hang bug on chrome/ webkit.
8910 var isSelectAll = false;
8912 if(this.inputEl().dom.selectionEnd > 0){
8913 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8915 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8916 event.preventDefault();
8921 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8923 event.preventDefault();
8924 // this is very hacky as keydown always get's upper case.
8926 var cc = String.fromCharCode(event.getCharCode());
8927 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8931 adjustWidth : function(tag, w){
8932 tag = tag.toLowerCase();
8933 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8934 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8938 if(tag == 'textarea'){
8941 }else if(Roo.isOpera){
8945 if(tag == 'textarea'){
8964 * @class Roo.bootstrap.TextArea
8965 * @extends Roo.bootstrap.Input
8966 * Bootstrap TextArea class
8967 * @cfg {Number} cols Specifies the visible width of a text area
8968 * @cfg {Number} rows Specifies the visible number of lines in a text area
8969 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8970 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8971 * @cfg {string} html text
8974 * Create a new TextArea
8975 * @param {Object} config The config object
8978 Roo.bootstrap.TextArea = function(config){
8979 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8983 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8993 getAutoCreate : function(){
8995 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9006 value : this.value || '',
9007 html: this.html || '',
9008 cls : 'form-control',
9009 placeholder : this.placeholder || ''
9013 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9014 input.maxLength = this.maxLength;
9018 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9022 input.cols = this.cols;
9025 if (this.readOnly) {
9026 input.readonly = true;
9030 input.name = this.name;
9034 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9038 ['xs','sm','md','lg'].map(function(size){
9039 if (settings[size]) {
9040 cfg.cls += ' col-' + size + '-' + settings[size];
9044 var inputblock = input;
9046 if(this.hasFeedback && !this.allowBlank){
9050 cls: 'glyphicon form-control-feedback'
9054 cls : 'has-feedback',
9063 if (this.before || this.after) {
9066 cls : 'input-group',
9070 inputblock.cn.push({
9072 cls : 'input-group-addon',
9077 inputblock.cn.push(input);
9079 if(this.hasFeedback && !this.allowBlank){
9080 inputblock.cls += ' has-feedback';
9081 inputblock.cn.push(feedback);
9085 inputblock.cn.push({
9087 cls : 'input-group-addon',
9094 if (align ==='left' && this.fieldLabel.length) {
9095 // Roo.log("left and has label");
9101 cls : 'control-label col-sm-' + this.labelWidth,
9102 html : this.fieldLabel
9106 cls : "col-sm-" + (12 - this.labelWidth),
9113 } else if ( this.fieldLabel.length) {
9114 // Roo.log(" label");
9119 //cls : 'input-group-addon',
9120 html : this.fieldLabel
9130 // Roo.log(" no label && no align");
9140 if (this.disabled) {
9141 input.disabled=true;
9148 * return the real textarea element.
9150 inputEl: function ()
9152 return this.el.select('textarea.form-control',true).first();
9156 * Clear any invalid styles/messages for this field
9158 clearInvalid : function()
9161 if(!this.el || this.preventMark){ // not rendered
9165 var label = this.el.select('label', true).first();
9166 var icon = this.el.select('i.fa-star', true).first();
9172 this.el.removeClass(this.invalidClass);
9174 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9176 var feedback = this.el.select('.form-control-feedback', true).first();
9179 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9184 this.fireEvent('valid', this);
9188 * Mark this field as valid
9190 markValid : function()
9192 if(!this.el || this.preventMark){ // not rendered
9196 this.el.removeClass([this.invalidClass, this.validClass]);
9198 var feedback = this.el.select('.form-control-feedback', true).first();
9201 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9204 if(this.disabled || this.allowBlank){
9208 var label = this.el.select('label', true).first();
9209 var icon = this.el.select('i.fa-star', true).first();
9215 this.el.addClass(this.validClass);
9217 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9219 var feedback = this.el.select('.form-control-feedback', true).first();
9222 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9223 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9228 this.fireEvent('valid', this);
9232 * Mark this field as invalid
9233 * @param {String} msg The validation message
9235 markInvalid : function(msg)
9237 if(!this.el || this.preventMark){ // not rendered
9241 this.el.removeClass([this.invalidClass, this.validClass]);
9243 var feedback = this.el.select('.form-control-feedback', true).first();
9246 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9249 if(this.disabled || this.allowBlank){
9253 var label = this.el.select('label', true).first();
9254 var icon = this.el.select('i.fa-star', true).first();
9256 if(!this.getValue().length && label && !icon){
9257 this.el.createChild({
9259 cls : 'text-danger fa fa-lg fa-star',
9260 tooltip : 'This field is required',
9261 style : 'margin-right:5px;'
9265 this.el.addClass(this.invalidClass);
9267 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9269 var feedback = this.el.select('.form-control-feedback', true).first();
9272 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9274 if(this.getValue().length || this.forceFeedback){
9275 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9282 this.fireEvent('invalid', this, msg);
9290 * trigger field - base class for combo..
9295 * @class Roo.bootstrap.TriggerField
9296 * @extends Roo.bootstrap.Input
9297 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9298 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9299 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9300 * for which you can provide a custom implementation. For example:
9302 var trigger = new Roo.bootstrap.TriggerField();
9303 trigger.onTriggerClick = myTriggerFn;
9304 trigger.applyTo('my-field');
9307 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9308 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9309 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9310 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9311 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9314 * Create a new TriggerField.
9315 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9316 * to the base TextField)
9318 Roo.bootstrap.TriggerField = function(config){
9319 this.mimicing = false;
9320 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9323 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9325 * @cfg {String} triggerClass A CSS class to apply to the trigger
9328 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9333 * @cfg {Boolean} removable (true|false) special filter default false
9337 /** @cfg {Boolean} grow @hide */
9338 /** @cfg {Number} growMin @hide */
9339 /** @cfg {Number} growMax @hide */
9345 autoSize: Roo.emptyFn,
9352 actionMode : 'wrap',
9357 getAutoCreate : function(){
9359 var align = this.labelAlign || this.parentLabelAlign();
9364 cls: 'form-group' //input-group
9371 type : this.inputType,
9372 cls : 'form-control',
9373 autocomplete: 'new-password',
9374 placeholder : this.placeholder || ''
9378 input.name = this.name;
9381 input.cls += ' input-' + this.size;
9384 if (this.disabled) {
9385 input.disabled=true;
9388 var inputblock = input;
9390 if(this.hasFeedback && !this.allowBlank){
9394 cls: 'glyphicon form-control-feedback'
9397 if(this.removable && !this.editable && !this.tickable){
9399 cls : 'has-feedback',
9405 cls : 'roo-combo-removable-btn close'
9412 cls : 'has-feedback',
9421 if(this.removable && !this.editable && !this.tickable){
9423 cls : 'roo-removable',
9429 cls : 'roo-combo-removable-btn close'
9436 if (this.before || this.after) {
9439 cls : 'input-group',
9443 inputblock.cn.push({
9445 cls : 'input-group-addon',
9450 inputblock.cn.push(input);
9452 if(this.hasFeedback && !this.allowBlank){
9453 inputblock.cls += ' has-feedback';
9454 inputblock.cn.push(feedback);
9458 inputblock.cn.push({
9460 cls : 'input-group-addon',
9473 cls: 'form-hidden-field'
9487 cls: 'form-hidden-field'
9491 cls: 'roo-select2-choices',
9495 cls: 'roo-select2-search-field',
9508 cls: 'roo-select2-container input-group',
9513 // cls: 'typeahead typeahead-long dropdown-menu',
9514 // style: 'display:none'
9519 if(!this.multiple && this.showToggleBtn){
9525 if (this.caret != false) {
9528 cls: 'fa fa-' + this.caret
9535 cls : 'input-group-addon btn dropdown-toggle',
9540 cls: 'combobox-clear',
9554 combobox.cls += ' roo-select2-container-multi';
9557 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9559 // Roo.log("left and has label");
9563 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9564 tooltip : 'This field is required'
9569 cls : 'control-label col-sm-' + this.labelWidth,
9570 html : this.fieldLabel
9574 cls : "col-sm-" + (12 - this.labelWidth),
9582 if(this.indicatorpos == 'right'){
9587 cls : 'control-label col-sm-' + this.labelWidth,
9588 html : this.fieldLabel
9593 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9594 tooltip : 'This field is required'
9597 cls : "col-sm-" + (12 - this.labelWidth),
9606 } else if ( this.fieldLabel.length) {
9607 // Roo.log(" label");
9611 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9612 tooltip : 'This field is required'
9616 //cls : 'input-group-addon',
9617 html : this.fieldLabel
9625 if(this.indicatorpos == 'right'){
9630 //cls : 'input-group-addon',
9631 html : this.fieldLabel
9636 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9637 tooltip : 'This field is required'
9648 // Roo.log(" no label && no align");
9655 ['xs','sm','md','lg'].map(function(size){
9656 if (settings[size]) {
9657 cfg.cls += ' col-' + size + '-' + settings[size];
9668 onResize : function(w, h){
9669 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9670 // if(typeof w == 'number'){
9671 // var x = w - this.trigger.getWidth();
9672 // this.inputEl().setWidth(this.adjustWidth('input', x));
9673 // this.trigger.setStyle('left', x+'px');
9678 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9681 getResizeEl : function(){
9682 return this.inputEl();
9686 getPositionEl : function(){
9687 return this.inputEl();
9691 alignErrorIcon : function(){
9692 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9696 initEvents : function(){
9700 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9701 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9702 if(!this.multiple && this.showToggleBtn){
9703 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9704 if(this.hideTrigger){
9705 this.trigger.setDisplayed(false);
9707 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9711 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9714 if(this.removable && !this.editable && !this.tickable){
9715 var close = this.closeTriggerEl();
9718 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9719 close.on('click', this.removeBtnClick, this, close);
9723 //this.trigger.addClassOnOver('x-form-trigger-over');
9724 //this.trigger.addClassOnClick('x-form-trigger-click');
9727 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9731 closeTriggerEl : function()
9733 var close = this.el.select('.roo-combo-removable-btn', true).first();
9734 return close ? close : false;
9737 removeBtnClick : function(e, h, el)
9741 if(this.fireEvent("remove", this) !== false){
9743 this.fireEvent("afterremove", this)
9747 createList : function()
9749 this.list = Roo.get(document.body).createChild({
9751 cls: 'typeahead typeahead-long dropdown-menu',
9752 style: 'display:none'
9755 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9760 initTrigger : function(){
9765 onDestroy : function(){
9767 this.trigger.removeAllListeners();
9768 // this.trigger.remove();
9771 // this.wrap.remove();
9773 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9777 onFocus : function(){
9778 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9781 this.wrap.addClass('x-trigger-wrap-focus');
9782 this.mimicing = true;
9783 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9784 if(this.monitorTab){
9785 this.el.on("keydown", this.checkTab, this);
9792 checkTab : function(e){
9793 if(e.getKey() == e.TAB){
9799 onBlur : function(){
9804 mimicBlur : function(e, t){
9806 if(!this.wrap.contains(t) && this.validateBlur()){
9813 triggerBlur : function(){
9814 this.mimicing = false;
9815 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9816 if(this.monitorTab){
9817 this.el.un("keydown", this.checkTab, this);
9819 //this.wrap.removeClass('x-trigger-wrap-focus');
9820 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9824 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9825 validateBlur : function(e, t){
9830 onDisable : function(){
9831 this.inputEl().dom.disabled = true;
9832 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9834 // this.wrap.addClass('x-item-disabled');
9839 onEnable : function(){
9840 this.inputEl().dom.disabled = false;
9841 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9843 // this.el.removeClass('x-item-disabled');
9848 onShow : function(){
9849 var ae = this.getActionEl();
9852 ae.dom.style.display = '';
9853 ae.dom.style.visibility = 'visible';
9859 onHide : function(){
9860 var ae = this.getActionEl();
9861 ae.dom.style.display = 'none';
9865 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9866 * by an implementing function.
9868 * @param {EventObject} e
9870 onTriggerClick : Roo.emptyFn
9874 * Ext JS Library 1.1.1
9875 * Copyright(c) 2006-2007, Ext JS, LLC.
9877 * Originally Released Under LGPL - original licence link has changed is not relivant.
9880 * <script type="text/javascript">
9885 * @class Roo.data.SortTypes
9887 * Defines the default sorting (casting?) comparison functions used when sorting data.
9889 Roo.data.SortTypes = {
9891 * Default sort that does nothing
9892 * @param {Mixed} s The value being converted
9893 * @return {Mixed} The comparison value
9900 * The regular expression used to strip tags
9904 stripTagsRE : /<\/?[^>]+>/gi,
9907 * Strips all HTML tags to sort on text only
9908 * @param {Mixed} s The value being converted
9909 * @return {String} The comparison value
9911 asText : function(s){
9912 return String(s).replace(this.stripTagsRE, "");
9916 * Strips all HTML tags to sort on text only - Case insensitive
9917 * @param {Mixed} s The value being converted
9918 * @return {String} The comparison value
9920 asUCText : function(s){
9921 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9925 * Case insensitive string
9926 * @param {Mixed} s The value being converted
9927 * @return {String} The comparison value
9929 asUCString : function(s) {
9930 return String(s).toUpperCase();
9935 * @param {Mixed} s The value being converted
9936 * @return {Number} The comparison value
9938 asDate : function(s) {
9942 if(s instanceof Date){
9945 return Date.parse(String(s));
9950 * @param {Mixed} s The value being converted
9951 * @return {Float} The comparison value
9953 asFloat : function(s) {
9954 var val = parseFloat(String(s).replace(/,/g, ""));
9963 * @param {Mixed} s The value being converted
9964 * @return {Number} The comparison value
9966 asInt : function(s) {
9967 var val = parseInt(String(s).replace(/,/g, ""));
9975 * Ext JS Library 1.1.1
9976 * Copyright(c) 2006-2007, Ext JS, LLC.
9978 * Originally Released Under LGPL - original licence link has changed is not relivant.
9981 * <script type="text/javascript">
9985 * @class Roo.data.Record
9986 * Instances of this class encapsulate both record <em>definition</em> information, and record
9987 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9988 * to access Records cached in an {@link Roo.data.Store} object.<br>
9990 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9991 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9994 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9996 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9997 * {@link #create}. The parameters are the same.
9998 * @param {Array} data An associative Array of data values keyed by the field name.
9999 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10000 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10001 * not specified an integer id is generated.
10003 Roo.data.Record = function(data, id){
10004 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10009 * Generate a constructor for a specific record layout.
10010 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10011 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10012 * Each field definition object may contain the following properties: <ul>
10013 * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
10014 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10015 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10016 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10017 * is being used, then this is a string containing the javascript expression to reference the data relative to
10018 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10019 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10020 * this may be omitted.</p></li>
10021 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10022 * <ul><li>auto (Default, implies no conversion)</li>
10027 * <li>date</li></ul></p></li>
10028 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10029 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10030 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10031 * by the Reader into an object that will be stored in the Record. It is passed the
10032 * following parameters:<ul>
10033 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10035 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10037 * <br>usage:<br><pre><code>
10038 var TopicRecord = Roo.data.Record.create(
10039 {name: 'title', mapping: 'topic_title'},
10040 {name: 'author', mapping: 'username'},
10041 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10042 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10043 {name: 'lastPoster', mapping: 'user2'},
10044 {name: 'excerpt', mapping: 'post_text'}
10047 var myNewRecord = new TopicRecord({
10048 title: 'Do my job please',
10051 lastPost: new Date(),
10052 lastPoster: 'Animal',
10053 excerpt: 'No way dude!'
10055 myStore.add(myNewRecord);
10060 Roo.data.Record.create = function(o){
10061 var f = function(){
10062 f.superclass.constructor.apply(this, arguments);
10064 Roo.extend(f, Roo.data.Record);
10065 var p = f.prototype;
10066 p.fields = new Roo.util.MixedCollection(false, function(field){
10069 for(var i = 0, len = o.length; i < len; i++){
10070 p.fields.add(new Roo.data.Field(o[i]));
10072 f.getField = function(name){
10073 return p.fields.get(name);
10078 Roo.data.Record.AUTO_ID = 1000;
10079 Roo.data.Record.EDIT = 'edit';
10080 Roo.data.Record.REJECT = 'reject';
10081 Roo.data.Record.COMMIT = 'commit';
10083 Roo.data.Record.prototype = {
10085 * Readonly flag - true if this record has been modified.
10094 join : function(store){
10095 this.store = store;
10099 * Set the named field to the specified value.
10100 * @param {String} name The name of the field to set.
10101 * @param {Object} value The value to set the field to.
10103 set : function(name, value){
10104 if(this.data[name] == value){
10108 if(!this.modified){
10109 this.modified = {};
10111 if(typeof this.modified[name] == 'undefined'){
10112 this.modified[name] = this.data[name];
10114 this.data[name] = value;
10115 if(!this.editing && this.store){
10116 this.store.afterEdit(this);
10121 * Get the value of the named field.
10122 * @param {String} name The name of the field to get the value of.
10123 * @return {Object} The value of the field.
10125 get : function(name){
10126 return this.data[name];
10130 beginEdit : function(){
10131 this.editing = true;
10132 this.modified = {};
10136 cancelEdit : function(){
10137 this.editing = false;
10138 delete this.modified;
10142 endEdit : function(){
10143 this.editing = false;
10144 if(this.dirty && this.store){
10145 this.store.afterEdit(this);
10150 * Usually called by the {@link Roo.data.Store} which owns the Record.
10151 * Rejects all changes made to the Record since either creation, or the last commit operation.
10152 * Modified fields are reverted to their original values.
10154 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10155 * of reject operations.
10157 reject : function(){
10158 var m = this.modified;
10160 if(typeof m[n] != "function"){
10161 this.data[n] = m[n];
10164 this.dirty = false;
10165 delete this.modified;
10166 this.editing = false;
10168 this.store.afterReject(this);
10173 * Usually called by the {@link Roo.data.Store} which owns the Record.
10174 * Commits all changes made to the Record since either creation, or the last commit operation.
10176 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10177 * of commit operations.
10179 commit : function(){
10180 this.dirty = false;
10181 delete this.modified;
10182 this.editing = false;
10184 this.store.afterCommit(this);
10189 hasError : function(){
10190 return this.error != null;
10194 clearError : function(){
10199 * Creates a copy of this record.
10200 * @param {String} id (optional) A new record id if you don't want to use this record's id
10203 copy : function(newId) {
10204 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10208 * Ext JS Library 1.1.1
10209 * Copyright(c) 2006-2007, Ext JS, LLC.
10211 * Originally Released Under LGPL - original licence link has changed is not relivant.
10214 * <script type="text/javascript">
10220 * @class Roo.data.Store
10221 * @extends Roo.util.Observable
10222 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10223 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10225 * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
10226 * has no knowledge of the format of the data returned by the Proxy.<br>
10228 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10229 * instances from the data object. These records are cached and made available through accessor functions.
10231 * Creates a new Store.
10232 * @param {Object} config A config object containing the objects needed for the Store to access data,
10233 * and read the data into Records.
10235 Roo.data.Store = function(config){
10236 this.data = new Roo.util.MixedCollection(false);
10237 this.data.getKey = function(o){
10240 this.baseParams = {};
10242 this.paramNames = {
10247 "multisort" : "_multisort"
10250 if(config && config.data){
10251 this.inlineData = config.data;
10252 delete config.data;
10255 Roo.apply(this, config);
10257 if(this.reader){ // reader passed
10258 this.reader = Roo.factory(this.reader, Roo.data);
10259 this.reader.xmodule = this.xmodule || false;
10260 if(!this.recordType){
10261 this.recordType = this.reader.recordType;
10263 if(this.reader.onMetaChange){
10264 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10268 if(this.recordType){
10269 this.fields = this.recordType.prototype.fields;
10271 this.modified = [];
10275 * @event datachanged
10276 * Fires when the data cache has changed, and a widget which is using this Store
10277 * as a Record cache should refresh its view.
10278 * @param {Store} this
10280 datachanged : true,
10282 * @event metachange
10283 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10284 * @param {Store} this
10285 * @param {Object} meta The JSON metadata
10290 * Fires when Records have been added to the Store
10291 * @param {Store} this
10292 * @param {Roo.data.Record[]} records The array of Records added
10293 * @param {Number} index The index at which the record(s) were added
10298 * Fires when a Record has been removed from the Store
10299 * @param {Store} this
10300 * @param {Roo.data.Record} record The Record that was removed
10301 * @param {Number} index The index at which the record was removed
10306 * Fires when a Record has been updated
10307 * @param {Store} this
10308 * @param {Roo.data.Record} record The Record that was updated
10309 * @param {String} operation The update operation being performed. Value may be one of:
10311 Roo.data.Record.EDIT
10312 Roo.data.Record.REJECT
10313 Roo.data.Record.COMMIT
10319 * Fires when the data cache has been cleared.
10320 * @param {Store} this
10324 * @event beforeload
10325 * Fires before a request is made for a new data object. If the beforeload handler returns false
10326 * the load action will be canceled.
10327 * @param {Store} this
10328 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10332 * @event beforeloadadd
10333 * Fires after a new set of Records has been loaded.
10334 * @param {Store} this
10335 * @param {Roo.data.Record[]} records The Records that were loaded
10336 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10338 beforeloadadd : true,
10341 * Fires after a new set of Records has been loaded, before they are added to the store.
10342 * @param {Store} this
10343 * @param {Roo.data.Record[]} records The Records that were loaded
10344 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10345 * @params {Object} return from reader
10349 * @event loadexception
10350 * Fires if an exception occurs in the Proxy during loading.
10351 * Called with the signature of the Proxy's "loadexception" event.
10352 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10355 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10356 * @param {Object} load options
10357 * @param {Object} jsonData from your request (normally this contains the Exception)
10359 loadexception : true
10363 this.proxy = Roo.factory(this.proxy, Roo.data);
10364 this.proxy.xmodule = this.xmodule || false;
10365 this.relayEvents(this.proxy, ["loadexception"]);
10367 this.sortToggle = {};
10368 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10370 Roo.data.Store.superclass.constructor.call(this);
10372 if(this.inlineData){
10373 this.loadData(this.inlineData);
10374 delete this.inlineData;
10378 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10380 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10381 * without a remote query - used by combo/forms at present.
10385 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10388 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10391 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10392 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10395 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10396 * on any HTTP request
10399 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10402 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10406 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10407 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10409 remoteSort : false,
10412 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10413 * loaded or when a record is removed. (defaults to false).
10415 pruneModifiedRecords : false,
10418 lastOptions : null,
10421 * Add Records to the Store and fires the add event.
10422 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10424 add : function(records){
10425 records = [].concat(records);
10426 for(var i = 0, len = records.length; i < len; i++){
10427 records[i].join(this);
10429 var index = this.data.length;
10430 this.data.addAll(records);
10431 this.fireEvent("add", this, records, index);
10435 * Remove a Record from the Store and fires the remove event.
10436 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10438 remove : function(record){
10439 var index = this.data.indexOf(record);
10440 this.data.removeAt(index);
10441 if(this.pruneModifiedRecords){
10442 this.modified.remove(record);
10444 this.fireEvent("remove", this, record, index);
10448 * Remove all Records from the Store and fires the clear event.
10450 removeAll : function(){
10452 if(this.pruneModifiedRecords){
10453 this.modified = [];
10455 this.fireEvent("clear", this);
10459 * Inserts Records to the Store at the given index and fires the add event.
10460 * @param {Number} index The start index at which to insert the passed Records.
10461 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10463 insert : function(index, records){
10464 records = [].concat(records);
10465 for(var i = 0, len = records.length; i < len; i++){
10466 this.data.insert(index, records[i]);
10467 records[i].join(this);
10469 this.fireEvent("add", this, records, index);
10473 * Get the index within the cache of the passed Record.
10474 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10475 * @return {Number} The index of the passed Record. Returns -1 if not found.
10477 indexOf : function(record){
10478 return this.data.indexOf(record);
10482 * Get the index within the cache of the Record with the passed id.
10483 * @param {String} id The id of the Record to find.
10484 * @return {Number} The index of the Record. Returns -1 if not found.
10486 indexOfId : function(id){
10487 return this.data.indexOfKey(id);
10491 * Get the Record with the specified id.
10492 * @param {String} id The id of the Record to find.
10493 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10495 getById : function(id){
10496 return this.data.key(id);
10500 * Get the Record at the specified index.
10501 * @param {Number} index The index of the Record to find.
10502 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10504 getAt : function(index){
10505 return this.data.itemAt(index);
10509 * Returns a range of Records between specified indices.
10510 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10511 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10512 * @return {Roo.data.Record[]} An array of Records
10514 getRange : function(start, end){
10515 return this.data.getRange(start, end);
10519 storeOptions : function(o){
10520 o = Roo.apply({}, o);
10523 this.lastOptions = o;
10527 * Loads the Record cache from the configured Proxy using the configured Reader.
10529 * If using remote paging, then the first load call must specify the <em>start</em>
10530 * and <em>limit</em> properties in the options.params property to establish the initial
10531 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10533 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10534 * and this call will return before the new data has been loaded. Perform any post-processing
10535 * in a callback function, or in a "load" event handler.</strong>
10537 * @param {Object} options An object containing properties which control loading options:<ul>
10538 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10539 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10540 * passed the following arguments:<ul>
10541 * <li>r : Roo.data.Record[]</li>
10542 * <li>options: Options object from the load call</li>
10543 * <li>success: Boolean success indicator</li></ul></li>
10544 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10545 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10548 load : function(options){
10549 options = options || {};
10550 if(this.fireEvent("beforeload", this, options) !== false){
10551 this.storeOptions(options);
10552 var p = Roo.apply(options.params || {}, this.baseParams);
10553 // if meta was not loaded from remote source.. try requesting it.
10554 if (!this.reader.metaFromRemote) {
10555 p._requestMeta = 1;
10557 if(this.sortInfo && this.remoteSort){
10558 var pn = this.paramNames;
10559 p[pn["sort"]] = this.sortInfo.field;
10560 p[pn["dir"]] = this.sortInfo.direction;
10562 if (this.multiSort) {
10563 var pn = this.paramNames;
10564 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10567 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10572 * Reloads the Record cache from the configured Proxy using the configured Reader and
10573 * the options from the last load operation performed.
10574 * @param {Object} options (optional) An object containing properties which may override the options
10575 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10576 * the most recently used options are reused).
10578 reload : function(options){
10579 this.load(Roo.applyIf(options||{}, this.lastOptions));
10583 // Called as a callback by the Reader during a load operation.
10584 loadRecords : function(o, options, success){
10585 if(!o || success === false){
10586 if(success !== false){
10587 this.fireEvent("load", this, [], options, o);
10589 if(options.callback){
10590 options.callback.call(options.scope || this, [], options, false);
10594 // if data returned failure - throw an exception.
10595 if (o.success === false) {
10596 // show a message if no listener is registered.
10597 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10598 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10600 // loadmask wil be hooked into this..
10601 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10604 var r = o.records, t = o.totalRecords || r.length;
10606 this.fireEvent("beforeloadadd", this, r, options, o);
10608 if(!options || options.add !== true){
10609 if(this.pruneModifiedRecords){
10610 this.modified = [];
10612 for(var i = 0, len = r.length; i < len; i++){
10616 this.data = this.snapshot;
10617 delete this.snapshot;
10620 this.data.addAll(r);
10621 this.totalLength = t;
10623 this.fireEvent("datachanged", this);
10625 this.totalLength = Math.max(t, this.data.length+r.length);
10628 this.fireEvent("load", this, r, options, o);
10629 if(options.callback){
10630 options.callback.call(options.scope || this, r, options, true);
10636 * Loads data from a passed data block. A Reader which understands the format of the data
10637 * must have been configured in the constructor.
10638 * @param {Object} data The data block from which to read the Records. The format of the data expected
10639 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10640 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10642 loadData : function(o, append){
10643 var r = this.reader.readRecords(o);
10644 this.loadRecords(r, {add: append}, true);
10648 * Gets the number of cached records.
10650 * <em>If using paging, this may not be the total size of the dataset. If the data object
10651 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10652 * the data set size</em>
10654 getCount : function(){
10655 return this.data.length || 0;
10659 * Gets the total number of records in the dataset as returned by the server.
10661 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10662 * the dataset size</em>
10664 getTotalCount : function(){
10665 return this.totalLength || 0;
10669 * Returns the sort state of the Store as an object with two properties:
10671 field {String} The name of the field by which the Records are sorted
10672 direction {String} The sort order, "ASC" or "DESC"
10675 getSortState : function(){
10676 return this.sortInfo;
10680 applySort : function(){
10681 if(this.sortInfo && !this.remoteSort){
10682 var s = this.sortInfo, f = s.field;
10683 var st = this.fields.get(f).sortType;
10684 var fn = function(r1, r2){
10685 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10686 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10688 this.data.sort(s.direction, fn);
10689 if(this.snapshot && this.snapshot != this.data){
10690 this.snapshot.sort(s.direction, fn);
10696 * Sets the default sort column and order to be used by the next load operation.
10697 * @param {String} fieldName The name of the field to sort by.
10698 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10700 setDefaultSort : function(field, dir){
10701 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10705 * Sort the Records.
10706 * If remote sorting is used, the sort is performed on the server, and the cache is
10707 * reloaded. If local sorting is used, the cache is sorted internally.
10708 * @param {String} fieldName The name of the field to sort by.
10709 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10711 sort : function(fieldName, dir){
10712 var f = this.fields.get(fieldName);
10714 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10716 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10717 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10722 this.sortToggle[f.name] = dir;
10723 this.sortInfo = {field: f.name, direction: dir};
10724 if(!this.remoteSort){
10726 this.fireEvent("datachanged", this);
10728 this.load(this.lastOptions);
10733 * Calls the specified function for each of the Records in the cache.
10734 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10735 * Returning <em>false</em> aborts and exits the iteration.
10736 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10738 each : function(fn, scope){
10739 this.data.each(fn, scope);
10743 * Gets all records modified since the last commit. Modified records are persisted across load operations
10744 * (e.g., during paging).
10745 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10747 getModifiedRecords : function(){
10748 return this.modified;
10752 createFilterFn : function(property, value, anyMatch){
10753 if(!value.exec){ // not a regex
10754 value = String(value);
10755 if(value.length == 0){
10758 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10760 return function(r){
10761 return value.test(r.data[property]);
10766 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10767 * @param {String} property A field on your records
10768 * @param {Number} start The record index to start at (defaults to 0)
10769 * @param {Number} end The last record index to include (defaults to length - 1)
10770 * @return {Number} The sum
10772 sum : function(property, start, end){
10773 var rs = this.data.items, v = 0;
10774 start = start || 0;
10775 end = (end || end === 0) ? end : rs.length-1;
10777 for(var i = start; i <= end; i++){
10778 v += (rs[i].data[property] || 0);
10784 * Filter the records by a specified property.
10785 * @param {String} field A field on your records
10786 * @param {String/RegExp} value Either a string that the field
10787 * should start with or a RegExp to test against the field
10788 * @param {Boolean} anyMatch True to match any part not just the beginning
10790 filter : function(property, value, anyMatch){
10791 var fn = this.createFilterFn(property, value, anyMatch);
10792 return fn ? this.filterBy(fn) : this.clearFilter();
10796 * Filter by a function. The specified function will be called with each
10797 * record in this data source. If the function returns true the record is included,
10798 * otherwise it is filtered.
10799 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10800 * @param {Object} scope (optional) The scope of the function (defaults to this)
10802 filterBy : function(fn, scope){
10803 this.snapshot = this.snapshot || this.data;
10804 this.data = this.queryBy(fn, scope||this);
10805 this.fireEvent("datachanged", this);
10809 * Query the records by a specified property.
10810 * @param {String} field A field on your records
10811 * @param {String/RegExp} value Either a string that the field
10812 * should start with or a RegExp to test against the field
10813 * @param {Boolean} anyMatch True to match any part not just the beginning
10814 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10816 query : function(property, value, anyMatch){
10817 var fn = this.createFilterFn(property, value, anyMatch);
10818 return fn ? this.queryBy(fn) : this.data.clone();
10822 * Query by a function. The specified function will be called with each
10823 * record in this data source. If the function returns true the record is included
10825 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10826 * @param {Object} scope (optional) The scope of the function (defaults to this)
10827 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10829 queryBy : function(fn, scope){
10830 var data = this.snapshot || this.data;
10831 return data.filterBy(fn, scope||this);
10835 * Collects unique values for a particular dataIndex from this store.
10836 * @param {String} dataIndex The property to collect
10837 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10838 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10839 * @return {Array} An array of the unique values
10841 collect : function(dataIndex, allowNull, bypassFilter){
10842 var d = (bypassFilter === true && this.snapshot) ?
10843 this.snapshot.items : this.data.items;
10844 var v, sv, r = [], l = {};
10845 for(var i = 0, len = d.length; i < len; i++){
10846 v = d[i].data[dataIndex];
10848 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10857 * Revert to a view of the Record cache with no filtering applied.
10858 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10860 clearFilter : function(suppressEvent){
10861 if(this.snapshot && this.snapshot != this.data){
10862 this.data = this.snapshot;
10863 delete this.snapshot;
10864 if(suppressEvent !== true){
10865 this.fireEvent("datachanged", this);
10871 afterEdit : function(record){
10872 if(this.modified.indexOf(record) == -1){
10873 this.modified.push(record);
10875 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10879 afterReject : function(record){
10880 this.modified.remove(record);
10881 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10885 afterCommit : function(record){
10886 this.modified.remove(record);
10887 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10891 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10892 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10894 commitChanges : function(){
10895 var m = this.modified.slice(0);
10896 this.modified = [];
10897 for(var i = 0, len = m.length; i < len; i++){
10903 * Cancel outstanding changes on all changed records.
10905 rejectChanges : function(){
10906 var m = this.modified.slice(0);
10907 this.modified = [];
10908 for(var i = 0, len = m.length; i < len; i++){
10913 onMetaChange : function(meta, rtype, o){
10914 this.recordType = rtype;
10915 this.fields = rtype.prototype.fields;
10916 delete this.snapshot;
10917 this.sortInfo = meta.sortInfo || this.sortInfo;
10918 this.modified = [];
10919 this.fireEvent('metachange', this, this.reader.meta);
10922 moveIndex : function(data, type)
10924 var index = this.indexOf(data);
10926 var newIndex = index + type;
10930 this.insert(newIndex, data);
10935 * Ext JS Library 1.1.1
10936 * Copyright(c) 2006-2007, Ext JS, LLC.
10938 * Originally Released Under LGPL - original licence link has changed is not relivant.
10941 * <script type="text/javascript">
10945 * @class Roo.data.SimpleStore
10946 * @extends Roo.data.Store
10947 * Small helper class to make creating Stores from Array data easier.
10948 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10949 * @cfg {Array} fields An array of field definition objects, or field name strings.
10950 * @cfg {Array} data The multi-dimensional array of data
10952 * @param {Object} config
10954 Roo.data.SimpleStore = function(config){
10955 Roo.data.SimpleStore.superclass.constructor.call(this, {
10957 reader: new Roo.data.ArrayReader({
10960 Roo.data.Record.create(config.fields)
10962 proxy : new Roo.data.MemoryProxy(config.data)
10966 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10968 * Ext JS Library 1.1.1
10969 * Copyright(c) 2006-2007, Ext JS, LLC.
10971 * Originally Released Under LGPL - original licence link has changed is not relivant.
10974 * <script type="text/javascript">
10979 * @extends Roo.data.Store
10980 * @class Roo.data.JsonStore
10981 * Small helper class to make creating Stores for JSON data easier. <br/>
10983 var store = new Roo.data.JsonStore({
10984 url: 'get-images.php',
10986 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10989 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10990 * JsonReader and HttpProxy (unless inline data is provided).</b>
10991 * @cfg {Array} fields An array of field definition objects, or field name strings.
10993 * @param {Object} config
10995 Roo.data.JsonStore = function(c){
10996 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10997 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10998 reader: new Roo.data.JsonReader(c, c.fields)
11001 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11003 * Ext JS Library 1.1.1
11004 * Copyright(c) 2006-2007, Ext JS, LLC.
11006 * Originally Released Under LGPL - original licence link has changed is not relivant.
11009 * <script type="text/javascript">
11013 Roo.data.Field = function(config){
11014 if(typeof config == "string"){
11015 config = {name: config};
11017 Roo.apply(this, config);
11020 this.type = "auto";
11023 var st = Roo.data.SortTypes;
11024 // named sortTypes are supported, here we look them up
11025 if(typeof this.sortType == "string"){
11026 this.sortType = st[this.sortType];
11029 // set default sortType for strings and dates
11030 if(!this.sortType){
11033 this.sortType = st.asUCString;
11036 this.sortType = st.asDate;
11039 this.sortType = st.none;
11044 var stripRe = /[\$,%]/g;
11046 // prebuilt conversion function for this field, instead of
11047 // switching every time we're reading a value
11049 var cv, dateFormat = this.dateFormat;
11054 cv = function(v){ return v; };
11057 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11061 return v !== undefined && v !== null && v !== '' ?
11062 parseInt(String(v).replace(stripRe, ""), 10) : '';
11067 return v !== undefined && v !== null && v !== '' ?
11068 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11073 cv = function(v){ return v === true || v === "true" || v == 1; };
11080 if(v instanceof Date){
11084 if(dateFormat == "timestamp"){
11085 return new Date(v*1000);
11087 return Date.parseDate(v, dateFormat);
11089 var parsed = Date.parse(v);
11090 return parsed ? new Date(parsed) : null;
11099 Roo.data.Field.prototype = {
11107 * Ext JS Library 1.1.1
11108 * Copyright(c) 2006-2007, Ext JS, LLC.
11110 * Originally Released Under LGPL - original licence link has changed is not relivant.
11113 * <script type="text/javascript">
11116 // Base class for reading structured data from a data source. This class is intended to be
11117 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11120 * @class Roo.data.DataReader
11121 * Base class for reading structured data from a data source. This class is intended to be
11122 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11125 Roo.data.DataReader = function(meta, recordType){
11129 this.recordType = recordType instanceof Array ?
11130 Roo.data.Record.create(recordType) : recordType;
11133 Roo.data.DataReader.prototype = {
11135 * Create an empty record
11136 * @param {Object} data (optional) - overlay some values
11137 * @return {Roo.data.Record} record created.
11139 newRow : function(d) {
11141 this.recordType.prototype.fields.each(function(c) {
11143 case 'int' : da[c.name] = 0; break;
11144 case 'date' : da[c.name] = new Date(); break;
11145 case 'float' : da[c.name] = 0.0; break;
11146 case 'boolean' : da[c.name] = false; break;
11147 default : da[c.name] = ""; break;
11151 return new this.recordType(Roo.apply(da, d));
11156 * Ext JS Library 1.1.1
11157 * Copyright(c) 2006-2007, Ext JS, LLC.
11159 * Originally Released Under LGPL - original licence link has changed is not relivant.
11162 * <script type="text/javascript">
11166 * @class Roo.data.DataProxy
11167 * @extends Roo.data.Observable
11168 * This class is an abstract base class for implementations which provide retrieval of
11169 * unformatted data objects.<br>
11171 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11172 * (of the appropriate type which knows how to parse the data object) to provide a block of
11173 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11175 * Custom implementations must implement the load method as described in
11176 * {@link Roo.data.HttpProxy#load}.
11178 Roo.data.DataProxy = function(){
11181 * @event beforeload
11182 * Fires before a network request is made to retrieve a data object.
11183 * @param {Object} This DataProxy object.
11184 * @param {Object} params The params parameter to the load function.
11189 * Fires before the load method's callback is called.
11190 * @param {Object} This DataProxy object.
11191 * @param {Object} o The data object.
11192 * @param {Object} arg The callback argument object passed to the load function.
11196 * @event loadexception
11197 * Fires if an Exception occurs during data retrieval.
11198 * @param {Object} This DataProxy object.
11199 * @param {Object} o The data object.
11200 * @param {Object} arg The callback argument object passed to the load function.
11201 * @param {Object} e The Exception.
11203 loadexception : true
11205 Roo.data.DataProxy.superclass.constructor.call(this);
11208 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11211 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11215 * Ext JS Library 1.1.1
11216 * Copyright(c) 2006-2007, Ext JS, LLC.
11218 * Originally Released Under LGPL - original licence link has changed is not relivant.
11221 * <script type="text/javascript">
11224 * @class Roo.data.MemoryProxy
11225 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11226 * to the Reader when its load method is called.
11228 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11230 Roo.data.MemoryProxy = function(data){
11234 Roo.data.MemoryProxy.superclass.constructor.call(this);
11238 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11241 * Load data from the requested source (in this case an in-memory
11242 * data object passed to the constructor), read the data object into
11243 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11244 * process that block using the passed callback.
11245 * @param {Object} params This parameter is not used by the MemoryProxy class.
11246 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11247 * object into a block of Roo.data.Records.
11248 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11249 * The function must be passed <ul>
11250 * <li>The Record block object</li>
11251 * <li>The "arg" argument from the load function</li>
11252 * <li>A boolean success indicator</li>
11254 * @param {Object} scope The scope in which to call the callback
11255 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11257 load : function(params, reader, callback, scope, arg){
11258 params = params || {};
11261 result = reader.readRecords(this.data);
11263 this.fireEvent("loadexception", this, arg, null, e);
11264 callback.call(scope, null, arg, false);
11267 callback.call(scope, result, arg, true);
11271 update : function(params, records){
11276 * Ext JS Library 1.1.1
11277 * Copyright(c) 2006-2007, Ext JS, LLC.
11279 * Originally Released Under LGPL - original licence link has changed is not relivant.
11282 * <script type="text/javascript">
11285 * @class Roo.data.HttpProxy
11286 * @extends Roo.data.DataProxy
11287 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11288 * configured to reference a certain URL.<br><br>
11290 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11291 * from which the running page was served.<br><br>
11293 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11295 * Be aware that to enable the browser to parse an XML document, the server must set
11296 * the Content-Type header in the HTTP response to "text/xml".
11298 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11299 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11300 * will be used to make the request.
11302 Roo.data.HttpProxy = function(conn){
11303 Roo.data.HttpProxy.superclass.constructor.call(this);
11304 // is conn a conn config or a real conn?
11306 this.useAjax = !conn || !conn.events;
11310 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11311 // thse are take from connection...
11314 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11317 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11318 * extra parameters to each request made by this object. (defaults to undefined)
11321 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11322 * to each request made by this object. (defaults to undefined)
11325 * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11328 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11331 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11337 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11341 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11342 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11343 * a finer-grained basis than the DataProxy events.
11345 getConnection : function(){
11346 return this.useAjax ? Roo.Ajax : this.conn;
11350 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11351 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11352 * process that block using the passed callback.
11353 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11354 * for the request to the remote server.
11355 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11356 * object into a block of Roo.data.Records.
11357 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11358 * The function must be passed <ul>
11359 * <li>The Record block object</li>
11360 * <li>The "arg" argument from the load function</li>
11361 * <li>A boolean success indicator</li>
11363 * @param {Object} scope The scope in which to call the callback
11364 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11366 load : function(params, reader, callback, scope, arg){
11367 if(this.fireEvent("beforeload", this, params) !== false){
11369 params : params || {},
11371 callback : callback,
11376 callback : this.loadResponse,
11380 Roo.applyIf(o, this.conn);
11381 if(this.activeRequest){
11382 Roo.Ajax.abort(this.activeRequest);
11384 this.activeRequest = Roo.Ajax.request(o);
11386 this.conn.request(o);
11389 callback.call(scope||this, null, arg, false);
11394 loadResponse : function(o, success, response){
11395 delete this.activeRequest;
11397 this.fireEvent("loadexception", this, o, response);
11398 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11403 result = o.reader.read(response);
11405 this.fireEvent("loadexception", this, o, response, e);
11406 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11410 this.fireEvent("load", this, o, o.request.arg);
11411 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11415 update : function(dataSet){
11420 updateResponse : function(dataSet){
11425 * Ext JS Library 1.1.1
11426 * Copyright(c) 2006-2007, Ext JS, LLC.
11428 * Originally Released Under LGPL - original licence link has changed is not relivant.
11431 * <script type="text/javascript">
11435 * @class Roo.data.ScriptTagProxy
11436 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11437 * other than the originating domain of the running page.<br><br>
11439 * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
11440 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11442 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11443 * source code that is used as the source inside a <script> tag.<br><br>
11445 * In order for the browser to process the returned data, the server must wrap the data object
11446 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11447 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11448 * depending on whether the callback name was passed:
11451 boolean scriptTag = false;
11452 String cb = request.getParameter("callback");
11455 response.setContentType("text/javascript");
11457 response.setContentType("application/x-json");
11459 Writer out = response.getWriter();
11461 out.write(cb + "(");
11463 out.print(dataBlock.toJsonString());
11470 * @param {Object} config A configuration object.
11472 Roo.data.ScriptTagProxy = function(config){
11473 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11474 Roo.apply(this, config);
11475 this.head = document.getElementsByTagName("head")[0];
11478 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11480 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11482 * @cfg {String} url The URL from which to request the data object.
11485 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11489 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11490 * the server the name of the callback function set up by the load call to process the returned data object.
11491 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11492 * javascript output which calls this named function passing the data object as its only parameter.
11494 callbackParam : "callback",
11496 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11497 * name to the request.
11502 * Load data from the configured URL, read the data object into
11503 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11504 * process that block using the passed callback.
11505 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11506 * for the request to the remote server.
11507 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11508 * object into a block of Roo.data.Records.
11509 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11510 * The function must be passed <ul>
11511 * <li>The Record block object</li>
11512 * <li>The "arg" argument from the load function</li>
11513 * <li>A boolean success indicator</li>
11515 * @param {Object} scope The scope in which to call the callback
11516 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11518 load : function(params, reader, callback, scope, arg){
11519 if(this.fireEvent("beforeload", this, params) !== false){
11521 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11523 var url = this.url;
11524 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11526 url += "&_dc=" + (new Date().getTime());
11528 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11531 cb : "stcCallback"+transId,
11532 scriptId : "stcScript"+transId,
11536 callback : callback,
11542 window[trans.cb] = function(o){
11543 conn.handleResponse(o, trans);
11546 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11548 if(this.autoAbort !== false){
11552 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11554 var script = document.createElement("script");
11555 script.setAttribute("src", url);
11556 script.setAttribute("type", "text/javascript");
11557 script.setAttribute("id", trans.scriptId);
11558 this.head.appendChild(script);
11560 this.trans = trans;
11562 callback.call(scope||this, null, arg, false);
11567 isLoading : function(){
11568 return this.trans ? true : false;
11572 * Abort the current server request.
11574 abort : function(){
11575 if(this.isLoading()){
11576 this.destroyTrans(this.trans);
11581 destroyTrans : function(trans, isLoaded){
11582 this.head.removeChild(document.getElementById(trans.scriptId));
11583 clearTimeout(trans.timeoutId);
11585 window[trans.cb] = undefined;
11587 delete window[trans.cb];
11590 // if hasn't been loaded, wait for load to remove it to prevent script error
11591 window[trans.cb] = function(){
11592 window[trans.cb] = undefined;
11594 delete window[trans.cb];
11601 handleResponse : function(o, trans){
11602 this.trans = false;
11603 this.destroyTrans(trans, true);
11606 result = trans.reader.readRecords(o);
11608 this.fireEvent("loadexception", this, o, trans.arg, e);
11609 trans.callback.call(trans.scope||window, null, trans.arg, false);
11612 this.fireEvent("load", this, o, trans.arg);
11613 trans.callback.call(trans.scope||window, result, trans.arg, true);
11617 handleFailure : function(trans){
11618 this.trans = false;
11619 this.destroyTrans(trans, false);
11620 this.fireEvent("loadexception", this, null, trans.arg);
11621 trans.callback.call(trans.scope||window, null, trans.arg, false);
11625 * Ext JS Library 1.1.1
11626 * Copyright(c) 2006-2007, Ext JS, LLC.
11628 * Originally Released Under LGPL - original licence link has changed is not relivant.
11631 * <script type="text/javascript">
11635 * @class Roo.data.JsonReader
11636 * @extends Roo.data.DataReader
11637 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11638 * based on mappings in a provided Roo.data.Record constructor.
11640 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11641 * in the reply previously.
11646 var RecordDef = Roo.data.Record.create([
11647 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11648 {name: 'occupation'} // This field will use "occupation" as the mapping.
11650 var myReader = new Roo.data.JsonReader({
11651 totalProperty: "results", // The property which contains the total dataset size (optional)
11652 root: "rows", // The property which contains an Array of row objects
11653 id: "id" // The property within each row object that provides an ID for the record (optional)
11657 * This would consume a JSON file like this:
11659 { 'results': 2, 'rows': [
11660 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11661 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11664 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11665 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11666 * paged from the remote server.
11667 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11668 * @cfg {String} root name of the property which contains the Array of row objects.
11669 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11670 * @cfg {Array} fields Array of field definition objects
11672 * Create a new JsonReader
11673 * @param {Object} meta Metadata configuration options
11674 * @param {Object} recordType Either an Array of field definition objects,
11675 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11677 Roo.data.JsonReader = function(meta, recordType){
11680 // set some defaults:
11681 Roo.applyIf(meta, {
11682 totalProperty: 'total',
11683 successProperty : 'success',
11688 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11690 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11693 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11694 * Used by Store query builder to append _requestMeta to params.
11697 metaFromRemote : false,
11699 * This method is only used by a DataProxy which has retrieved data from a remote server.
11700 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11701 * @return {Object} data A data block which is used by an Roo.data.Store object as
11702 * a cache of Roo.data.Records.
11704 read : function(response){
11705 var json = response.responseText;
11707 var o = /* eval:var:o */ eval("("+json+")");
11709 throw {message: "JsonReader.read: Json object not found"};
11715 this.metaFromRemote = true;
11716 this.meta = o.metaData;
11717 this.recordType = Roo.data.Record.create(o.metaData.fields);
11718 this.onMetaChange(this.meta, this.recordType, o);
11720 return this.readRecords(o);
11723 // private function a store will implement
11724 onMetaChange : function(meta, recordType, o){
11731 simpleAccess: function(obj, subsc) {
11738 getJsonAccessor: function(){
11740 return function(expr) {
11742 return(re.test(expr))
11743 ? new Function("obj", "return obj." + expr)
11748 return Roo.emptyFn;
11753 * Create a data block containing Roo.data.Records from an XML document.
11754 * @param {Object} o An object which contains an Array of row objects in the property specified
11755 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11756 * which contains the total size of the dataset.
11757 * @return {Object} data A data block which is used by an Roo.data.Store object as
11758 * a cache of Roo.data.Records.
11760 readRecords : function(o){
11762 * After any data loads, the raw JSON data is available for further custom processing.
11766 var s = this.meta, Record = this.recordType,
11767 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11769 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11771 if(s.totalProperty) {
11772 this.getTotal = this.getJsonAccessor(s.totalProperty);
11774 if(s.successProperty) {
11775 this.getSuccess = this.getJsonAccessor(s.successProperty);
11777 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11779 var g = this.getJsonAccessor(s.id);
11780 this.getId = function(rec) {
11782 return (r === undefined || r === "") ? null : r;
11785 this.getId = function(){return null;};
11788 for(var jj = 0; jj < fl; jj++){
11790 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11791 this.ef[jj] = this.getJsonAccessor(map);
11795 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11796 if(s.totalProperty){
11797 var vt = parseInt(this.getTotal(o), 10);
11802 if(s.successProperty){
11803 var vs = this.getSuccess(o);
11804 if(vs === false || vs === 'false'){
11809 for(var i = 0; i < c; i++){
11812 var id = this.getId(n);
11813 for(var j = 0; j < fl; j++){
11815 var v = this.ef[j](n);
11817 Roo.log('missing convert for ' + f.name);
11821 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11823 var record = new Record(values, id);
11825 records[i] = record;
11831 totalRecords : totalRecords
11836 * Ext JS Library 1.1.1
11837 * Copyright(c) 2006-2007, Ext JS, LLC.
11839 * Originally Released Under LGPL - original licence link has changed is not relivant.
11842 * <script type="text/javascript">
11846 * @class Roo.data.ArrayReader
11847 * @extends Roo.data.DataReader
11848 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11849 * Each element of that Array represents a row of data fields. The
11850 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11851 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11855 var RecordDef = Roo.data.Record.create([
11856 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11857 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11859 var myReader = new Roo.data.ArrayReader({
11860 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11864 * This would consume an Array like this:
11866 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11868 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11870 * Create a new JsonReader
11871 * @param {Object} meta Metadata configuration options.
11872 * @param {Object} recordType Either an Array of field definition objects
11873 * as specified to {@link Roo.data.Record#create},
11874 * or an {@link Roo.data.Record} object
11875 * created using {@link Roo.data.Record#create}.
11877 Roo.data.ArrayReader = function(meta, recordType){
11878 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11881 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11883 * Create a data block containing Roo.data.Records from an XML document.
11884 * @param {Object} o An Array of row objects which represents the dataset.
11885 * @return {Object} data A data block which is used by an Roo.data.Store object as
11886 * a cache of Roo.data.Records.
11888 readRecords : function(o){
11889 var sid = this.meta ? this.meta.id : null;
11890 var recordType = this.recordType, fields = recordType.prototype.fields;
11893 for(var i = 0; i < root.length; i++){
11896 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11897 for(var j = 0, jlen = fields.length; j < jlen; j++){
11898 var f = fields.items[j];
11899 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11900 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11902 values[f.name] = v;
11904 var record = new recordType(values, id);
11906 records[records.length] = record;
11910 totalRecords : records.length
11919 * @class Roo.bootstrap.ComboBox
11920 * @extends Roo.bootstrap.TriggerField
11921 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11922 * @cfg {Boolean} append (true|false) default false
11923 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11924 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11925 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11926 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11927 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11928 * @cfg {Boolean} animate default true
11929 * @cfg {Boolean} emptyResultText only for touch device
11930 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11932 * Create a new ComboBox.
11933 * @param {Object} config Configuration options
11935 Roo.bootstrap.ComboBox = function(config){
11936 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11940 * Fires when the dropdown list is expanded
11941 * @param {Roo.bootstrap.ComboBox} combo This combo box
11946 * Fires when the dropdown list is collapsed
11947 * @param {Roo.bootstrap.ComboBox} combo This combo box
11951 * @event beforeselect
11952 * Fires before a list item is selected. Return false to cancel the selection.
11953 * @param {Roo.bootstrap.ComboBox} combo This combo box
11954 * @param {Roo.data.Record} record The data record returned from the underlying store
11955 * @param {Number} index The index of the selected item in the dropdown list
11957 'beforeselect' : true,
11960 * Fires when a list item is selected
11961 * @param {Roo.bootstrap.ComboBox} combo This combo box
11962 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11963 * @param {Number} index The index of the selected item in the dropdown list
11967 * @event beforequery
11968 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11969 * The event object passed has these properties:
11970 * @param {Roo.bootstrap.ComboBox} combo This combo box
11971 * @param {String} query The query
11972 * @param {Boolean} forceAll true to force "all" query
11973 * @param {Boolean} cancel true to cancel the query
11974 * @param {Object} e The query event object
11976 'beforequery': true,
11979 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11980 * @param {Roo.bootstrap.ComboBox} combo This combo box
11985 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11986 * @param {Roo.bootstrap.ComboBox} combo This combo box
11987 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11992 * Fires when the remove value from the combobox array
11993 * @param {Roo.bootstrap.ComboBox} combo This combo box
11997 * @event afterremove
11998 * Fires when the remove value from the combobox array
11999 * @param {Roo.bootstrap.ComboBox} combo This combo box
12001 'afterremove' : true,
12003 * @event specialfilter
12004 * Fires when specialfilter
12005 * @param {Roo.bootstrap.ComboBox} combo This combo box
12007 'specialfilter' : true,
12010 * Fires when tick the element
12011 * @param {Roo.bootstrap.ComboBox} combo This combo box
12015 * @event touchviewdisplay
12016 * Fires when touch view require special display (default is using displayField)
12017 * @param {Roo.bootstrap.ComboBox} combo This combo box
12018 * @param {Object} cfg set html .
12020 'touchviewdisplay' : true
12025 this.tickItems = [];
12027 this.selectedIndex = -1;
12028 if(this.mode == 'local'){
12029 if(config.queryDelay === undefined){
12030 this.queryDelay = 10;
12032 if(config.minChars === undefined){
12038 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12041 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12042 * rendering into an Roo.Editor, defaults to false)
12045 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12046 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12049 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12052 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12053 * the dropdown list (defaults to undefined, with no header element)
12057 * @cfg {String/Roo.Template} tpl The template to use to render the output
12061 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12063 listWidth: undefined,
12065 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12066 * mode = 'remote' or 'text' if mode = 'local')
12068 displayField: undefined,
12071 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12072 * mode = 'remote' or 'value' if mode = 'local').
12073 * Note: use of a valueField requires the user make a selection
12074 * in order for a value to be mapped.
12076 valueField: undefined,
12078 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12083 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12084 * field's data value (defaults to the underlying DOM element's name)
12086 hiddenName: undefined,
12088 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12092 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12094 selectedClass: 'active',
12097 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12101 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12102 * anchor positions (defaults to 'tl-bl')
12104 listAlign: 'tl-bl?',
12106 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12110 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12111 * query specified by the allQuery config option (defaults to 'query')
12113 triggerAction: 'query',
12115 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12116 * (defaults to 4, does not apply if editable = false)
12120 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12121 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12125 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12126 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12130 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12131 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12135 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12136 * when editable = true (defaults to false)
12138 selectOnFocus:false,
12140 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12142 queryParam: 'query',
12144 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12145 * when mode = 'remote' (defaults to 'Loading...')
12147 loadingText: 'Loading...',
12149 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12153 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12157 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12158 * traditional select (defaults to true)
12162 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12166 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12170 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12171 * listWidth has a higher value)
12175 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12176 * allow the user to set arbitrary text into the field (defaults to false)
12178 forceSelection:false,
12180 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12181 * if typeAhead = true (defaults to 250)
12183 typeAheadDelay : 250,
12185 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12186 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12188 valueNotFoundText : undefined,
12190 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12192 blockFocus : false,
12195 * @cfg {Boolean} disableClear Disable showing of clear button.
12197 disableClear : false,
12199 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12201 alwaysQuery : false,
12204 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12209 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12211 invalidClass : "has-warning",
12214 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12216 validClass : "has-success",
12219 * @cfg {Boolean} specialFilter (true|false) special filter default false
12221 specialFilter : false,
12224 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12226 mobileTouchView : true,
12238 btnPosition : 'right',
12239 triggerList : true,
12240 showToggleBtn : true,
12242 emptyResultText: 'Empty',
12243 triggerText : 'Select',
12245 // element that contains real text value.. (when hidden is used..)
12247 getAutoCreate : function()
12255 if(Roo.isTouch && this.mobileTouchView){
12256 cfg = this.getAutoCreateTouchView();
12263 if(!this.tickable){
12264 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12269 * ComboBox with tickable selections
12272 var align = this.labelAlign || this.parentLabelAlign();
12275 cls : 'form-group roo-combobox-tickable' //input-group
12280 cls : 'tickable-buttons',
12285 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12286 html : this.triggerText
12292 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12299 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12306 buttons.cn.unshift({
12308 cls: 'roo-select2-search-field-input'
12314 Roo.each(buttons.cn, function(c){
12316 c.cls += ' btn-' + _this.size;
12319 if (_this.disabled) {
12330 cls: 'form-hidden-field'
12334 cls: 'roo-select2-choices',
12338 cls: 'roo-select2-search-field',
12350 cls: 'roo-select2-container input-group roo-select2-container-multi',
12355 // cls: 'typeahead typeahead-long dropdown-menu',
12356 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12361 if(this.hasFeedback && !this.allowBlank){
12365 cls: 'glyphicon form-control-feedback'
12368 combobox.cn.push(feedback);
12371 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12373 // Roo.log("left and has label");
12377 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12378 tooltip : 'This field is required'
12383 cls : 'control-label col-sm-' + this.labelWidth,
12384 html : this.fieldLabel
12388 cls : "col-sm-" + (12 - this.labelWidth),
12396 if(this.indicatorpos == 'right'){
12402 cls : 'control-label col-sm-' + this.labelWidth,
12403 html : this.fieldLabel
12408 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12409 tooltip : 'This field is required'
12412 cls : "col-sm-" + (12 - this.labelWidth),
12423 } else if ( this.fieldLabel.length) {
12424 // Roo.log(" label");
12428 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12429 tooltip : 'This field is required'
12433 //cls : 'input-group-addon',
12434 html : this.fieldLabel
12442 if(this.indicatorpos == 'right'){
12447 //cls : 'input-group-addon',
12448 html : this.fieldLabel
12454 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12455 tooltip : 'This field is required'
12466 // Roo.log(" no label && no align");
12473 ['xs','sm','md','lg'].map(function(size){
12474 if (settings[size]) {
12475 cfg.cls += ' col-' + size + '-' + settings[size];
12483 _initEventsCalled : false,
12486 initEvents: function()
12489 if (this._initEventsCalled) { // as we call render... prevent looping...
12492 this._initEventsCalled = true;
12495 throw "can not find store for combo";
12498 this.store = Roo.factory(this.store, Roo.data);
12500 // if we are building from html. then this element is so complex, that we can not really
12501 // use the rendered HTML.
12502 // so we have to trash and replace the previous code.
12503 if (Roo.XComponent.build_from_html) {
12505 // remove this element....
12506 var e = this.el.dom, k=0;
12507 while (e ) { e = e.previousSibling; ++k;}
12512 this.rendered = false;
12514 this.render(this.parent().getChildContainer(true), k);
12525 if(Roo.isTouch && this.mobileTouchView){
12526 this.initTouchView();
12531 this.initTickableEvents();
12535 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12537 if(this.hiddenName){
12539 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12541 this.hiddenField.dom.value =
12542 this.hiddenValue !== undefined ? this.hiddenValue :
12543 this.value !== undefined ? this.value : '';
12545 // prevent input submission
12546 this.el.dom.removeAttribute('name');
12547 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12552 // this.el.dom.setAttribute('autocomplete', 'off');
12555 var cls = 'x-combo-list';
12557 //this.list = new Roo.Layer({
12558 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12564 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12565 _this.list.setWidth(lw);
12568 this.list.on('mouseover', this.onViewOver, this);
12569 this.list.on('mousemove', this.onViewMove, this);
12571 this.list.on('scroll', this.onViewScroll, this);
12574 this.list.swallowEvent('mousewheel');
12575 this.assetHeight = 0;
12578 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12579 this.assetHeight += this.header.getHeight();
12582 this.innerList = this.list.createChild({cls:cls+'-inner'});
12583 this.innerList.on('mouseover', this.onViewOver, this);
12584 this.innerList.on('mousemove', this.onViewMove, this);
12585 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12587 if(this.allowBlank && !this.pageSize && !this.disableClear){
12588 this.footer = this.list.createChild({cls:cls+'-ft'});
12589 this.pageTb = new Roo.Toolbar(this.footer);
12593 this.footer = this.list.createChild({cls:cls+'-ft'});
12594 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12595 {pageSize: this.pageSize});
12599 if (this.pageTb && this.allowBlank && !this.disableClear) {
12601 this.pageTb.add(new Roo.Toolbar.Fill(), {
12602 cls: 'x-btn-icon x-btn-clear',
12604 handler: function()
12607 _this.clearValue();
12608 _this.onSelect(false, -1);
12613 this.assetHeight += this.footer.getHeight();
12618 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12621 this.view = new Roo.View(this.list, this.tpl, {
12622 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12624 //this.view.wrapEl.setDisplayed(false);
12625 this.view.on('click', this.onViewClick, this);
12629 this.store.on('beforeload', this.onBeforeLoad, this);
12630 this.store.on('load', this.onLoad, this);
12631 this.store.on('loadexception', this.onLoadException, this);
12633 if(this.resizable){
12634 this.resizer = new Roo.Resizable(this.list, {
12635 pinned:true, handles:'se'
12637 this.resizer.on('resize', function(r, w, h){
12638 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12639 this.listWidth = w;
12640 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12641 this.restrictHeight();
12643 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12646 if(!this.editable){
12647 this.editable = true;
12648 this.setEditable(false);
12653 if (typeof(this.events.add.listeners) != 'undefined') {
12655 this.addicon = this.wrap.createChild(
12656 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12658 this.addicon.on('click', function(e) {
12659 this.fireEvent('add', this);
12662 if (typeof(this.events.edit.listeners) != 'undefined') {
12664 this.editicon = this.wrap.createChild(
12665 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12666 if (this.addicon) {
12667 this.editicon.setStyle('margin-left', '40px');
12669 this.editicon.on('click', function(e) {
12671 // we fire even if inothing is selected..
12672 this.fireEvent('edit', this, this.lastData );
12678 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12679 "up" : function(e){
12680 this.inKeyMode = true;
12684 "down" : function(e){
12685 if(!this.isExpanded()){
12686 this.onTriggerClick();
12688 this.inKeyMode = true;
12693 "enter" : function(e){
12694 // this.onViewClick();
12698 if(this.fireEvent("specialkey", this, e)){
12699 this.onViewClick(false);
12705 "esc" : function(e){
12709 "tab" : function(e){
12712 if(this.fireEvent("specialkey", this, e)){
12713 this.onViewClick(false);
12721 doRelay : function(foo, bar, hname){
12722 if(hname == 'down' || this.scope.isExpanded()){
12723 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12732 this.queryDelay = Math.max(this.queryDelay || 10,
12733 this.mode == 'local' ? 10 : 250);
12736 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12738 if(this.typeAhead){
12739 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12741 if(this.editable !== false){
12742 this.inputEl().on("keyup", this.onKeyUp, this);
12744 if(this.forceSelection){
12745 this.inputEl().on('blur', this.doForce, this);
12749 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12750 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12754 initTickableEvents: function()
12758 if(this.hiddenName){
12760 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12762 this.hiddenField.dom.value =
12763 this.hiddenValue !== undefined ? this.hiddenValue :
12764 this.value !== undefined ? this.value : '';
12766 // prevent input submission
12767 this.el.dom.removeAttribute('name');
12768 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12773 // this.list = this.el.select('ul.dropdown-menu',true).first();
12775 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12776 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12777 if(this.triggerList){
12778 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12781 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12782 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12784 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12785 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12787 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12788 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12790 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12791 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12792 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12795 this.cancelBtn.hide();
12800 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12801 _this.list.setWidth(lw);
12804 this.list.on('mouseover', this.onViewOver, this);
12805 this.list.on('mousemove', this.onViewMove, this);
12807 this.list.on('scroll', this.onViewScroll, this);
12810 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
12813 this.view = new Roo.View(this.list, this.tpl, {
12814 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12817 //this.view.wrapEl.setDisplayed(false);
12818 this.view.on('click', this.onViewClick, this);
12822 this.store.on('beforeload', this.onBeforeLoad, this);
12823 this.store.on('load', this.onLoad, this);
12824 this.store.on('loadexception', this.onLoadException, this);
12827 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12828 "up" : function(e){
12829 this.inKeyMode = true;
12833 "down" : function(e){
12834 this.inKeyMode = true;
12838 "enter" : function(e){
12839 if(this.fireEvent("specialkey", this, e)){
12840 this.onViewClick(false);
12846 "esc" : function(e){
12847 this.onTickableFooterButtonClick(e, false, false);
12850 "tab" : function(e){
12851 this.fireEvent("specialkey", this, e);
12853 this.onTickableFooterButtonClick(e, false, false);
12860 doRelay : function(e, fn, key){
12861 if(this.scope.isExpanded()){
12862 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12871 this.queryDelay = Math.max(this.queryDelay || 10,
12872 this.mode == 'local' ? 10 : 250);
12875 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12877 if(this.typeAhead){
12878 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12881 if(this.editable !== false){
12882 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12887 onDestroy : function(){
12889 this.view.setStore(null);
12890 this.view.el.removeAllListeners();
12891 this.view.el.remove();
12892 this.view.purgeListeners();
12895 this.list.dom.innerHTML = '';
12899 this.store.un('beforeload', this.onBeforeLoad, this);
12900 this.store.un('load', this.onLoad, this);
12901 this.store.un('loadexception', this.onLoadException, this);
12903 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12907 fireKey : function(e){
12908 if(e.isNavKeyPress() && !this.list.isVisible()){
12909 this.fireEvent("specialkey", this, e);
12914 onResize: function(w, h){
12915 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12917 // if(typeof w != 'number'){
12918 // // we do not handle it!?!?
12921 // var tw = this.trigger.getWidth();
12922 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12923 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12925 // this.inputEl().setWidth( this.adjustWidth('input', x));
12927 // //this.trigger.setStyle('left', x+'px');
12929 // if(this.list && this.listWidth === undefined){
12930 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12931 // this.list.setWidth(lw);
12932 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12940 * Allow or prevent the user from directly editing the field text. If false is passed,
12941 * the user will only be able to select from the items defined in the dropdown list. This method
12942 * is the runtime equivalent of setting the 'editable' config option at config time.
12943 * @param {Boolean} value True to allow the user to directly edit the field text
12945 setEditable : function(value){
12946 if(value == this.editable){
12949 this.editable = value;
12951 this.inputEl().dom.setAttribute('readOnly', true);
12952 this.inputEl().on('mousedown', this.onTriggerClick, this);
12953 this.inputEl().addClass('x-combo-noedit');
12955 this.inputEl().dom.setAttribute('readOnly', false);
12956 this.inputEl().un('mousedown', this.onTriggerClick, this);
12957 this.inputEl().removeClass('x-combo-noedit');
12963 onBeforeLoad : function(combo,opts){
12964 if(!this.hasFocus){
12968 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12970 this.restrictHeight();
12971 this.selectedIndex = -1;
12975 onLoad : function(){
12977 this.hasQuery = false;
12979 if(!this.hasFocus){
12983 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12984 this.loading.hide();
12987 if(this.store.getCount() > 0){
12989 this.restrictHeight();
12990 if(this.lastQuery == this.allQuery){
12991 if(this.editable && !this.tickable){
12992 this.inputEl().dom.select();
12996 !this.selectByValue(this.value, true) &&
12999 !this.store.lastOptions ||
13000 typeof(this.store.lastOptions.add) == 'undefined' ||
13001 this.store.lastOptions.add != true
13004 this.select(0, true);
13007 if(this.autoFocus){
13010 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13011 this.taTask.delay(this.typeAheadDelay);
13015 this.onEmptyResults();
13021 onLoadException : function()
13023 this.hasQuery = false;
13025 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13026 this.loading.hide();
13029 if(this.tickable && this.editable){
13034 // only causes errors at present
13035 //Roo.log(this.store.reader.jsonData);
13036 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13038 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13044 onTypeAhead : function(){
13045 if(this.store.getCount() > 0){
13046 var r = this.store.getAt(0);
13047 var newValue = r.data[this.displayField];
13048 var len = newValue.length;
13049 var selStart = this.getRawValue().length;
13051 if(selStart != len){
13052 this.setRawValue(newValue);
13053 this.selectText(selStart, newValue.length);
13059 onSelect : function(record, index){
13061 if(this.fireEvent('beforeselect', this, record, index) !== false){
13063 this.setFromData(index > -1 ? record.data : false);
13066 this.fireEvent('select', this, record, index);
13071 * Returns the currently selected field value or empty string if no value is set.
13072 * @return {String} value The selected value
13074 getValue : function(){
13077 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13080 if(this.valueField){
13081 return typeof this.value != 'undefined' ? this.value : '';
13083 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13088 * Clears any text/value currently set in the field
13090 clearValue : function(){
13091 if(this.hiddenField){
13092 this.hiddenField.dom.value = '';
13095 this.setRawValue('');
13096 this.lastSelectionText = '';
13097 this.lastData = false;
13099 var close = this.closeTriggerEl();
13110 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13111 * will be displayed in the field. If the value does not match the data value of an existing item,
13112 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13113 * Otherwise the field will be blank (although the value will still be set).
13114 * @param {String} value The value to match
13116 setValue : function(v){
13123 if(this.valueField){
13124 var r = this.findRecord(this.valueField, v);
13126 text = r.data[this.displayField];
13127 }else if(this.valueNotFoundText !== undefined){
13128 text = this.valueNotFoundText;
13131 this.lastSelectionText = text;
13132 if(this.hiddenField){
13133 this.hiddenField.dom.value = v;
13135 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13138 var close = this.closeTriggerEl();
13141 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13147 * @property {Object} the last set data for the element
13152 * Sets the value of the field based on a object which is related to the record format for the store.
13153 * @param {Object} value the value to set as. or false on reset?
13155 setFromData : function(o){
13162 var dv = ''; // display value
13163 var vv = ''; // value value..
13165 if (this.displayField) {
13166 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13168 // this is an error condition!!!
13169 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13172 if(this.valueField){
13173 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13176 var close = this.closeTriggerEl();
13179 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13182 if(this.hiddenField){
13183 this.hiddenField.dom.value = vv;
13185 this.lastSelectionText = dv;
13186 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13190 // no hidden field.. - we store the value in 'value', but still display
13191 // display field!!!!
13192 this.lastSelectionText = dv;
13193 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13200 reset : function(){
13201 // overridden so that last data is reset..
13208 this.setValue(this.originalValue);
13209 //this.clearInvalid();
13210 this.lastData = false;
13212 this.view.clearSelections();
13218 findRecord : function(prop, value){
13220 if(this.store.getCount() > 0){
13221 this.store.each(function(r){
13222 if(r.data[prop] == value){
13232 getName: function()
13234 // returns hidden if it's set..
13235 if (!this.rendered) {return ''};
13236 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13240 onViewMove : function(e, t){
13241 this.inKeyMode = false;
13245 onViewOver : function(e, t){
13246 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13249 var item = this.view.findItemFromChild(t);
13252 var index = this.view.indexOf(item);
13253 this.select(index, false);
13258 onViewClick : function(view, doFocus, el, e)
13260 var index = this.view.getSelectedIndexes()[0];
13262 var r = this.store.getAt(index);
13266 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13273 Roo.each(this.tickItems, function(v,k){
13275 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13277 _this.tickItems.splice(k, 1);
13279 if(typeof(e) == 'undefined' && view == false){
13280 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13292 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13293 this.tickItems.push(r.data);
13296 if(typeof(e) == 'undefined' && view == false){
13297 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13304 this.onSelect(r, index);
13306 if(doFocus !== false && !this.blockFocus){
13307 this.inputEl().focus();
13312 restrictHeight : function(){
13313 //this.innerList.dom.style.height = '';
13314 //var inner = this.innerList.dom;
13315 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13316 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13317 //this.list.beginUpdate();
13318 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13319 this.list.alignTo(this.inputEl(), this.listAlign);
13320 this.list.alignTo(this.inputEl(), this.listAlign);
13321 //this.list.endUpdate();
13325 onEmptyResults : function(){
13327 if(this.tickable && this.editable){
13328 this.restrictHeight();
13336 * Returns true if the dropdown list is expanded, else false.
13338 isExpanded : function(){
13339 return this.list.isVisible();
13343 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13344 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13345 * @param {String} value The data value of the item to select
13346 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13347 * selected item if it is not currently in view (defaults to true)
13348 * @return {Boolean} True if the value matched an item in the list, else false
13350 selectByValue : function(v, scrollIntoView){
13351 if(v !== undefined && v !== null){
13352 var r = this.findRecord(this.valueField || this.displayField, v);
13354 this.select(this.store.indexOf(r), scrollIntoView);
13362 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13363 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13364 * @param {Number} index The zero-based index of the list item to select
13365 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13366 * selected item if it is not currently in view (defaults to true)
13368 select : function(index, scrollIntoView){
13369 this.selectedIndex = index;
13370 this.view.select(index);
13371 if(scrollIntoView !== false){
13372 var el = this.view.getNode(index);
13374 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13377 this.list.scrollChildIntoView(el, false);
13383 selectNext : function(){
13384 var ct = this.store.getCount();
13386 if(this.selectedIndex == -1){
13388 }else if(this.selectedIndex < ct-1){
13389 this.select(this.selectedIndex+1);
13395 selectPrev : function(){
13396 var ct = this.store.getCount();
13398 if(this.selectedIndex == -1){
13400 }else if(this.selectedIndex != 0){
13401 this.select(this.selectedIndex-1);
13407 onKeyUp : function(e){
13408 if(this.editable !== false && !e.isSpecialKey()){
13409 this.lastKey = e.getKey();
13410 this.dqTask.delay(this.queryDelay);
13415 validateBlur : function(){
13416 return !this.list || !this.list.isVisible();
13420 initQuery : function(){
13422 var v = this.getRawValue();
13424 if(this.tickable && this.editable){
13425 v = this.tickableInputEl().getValue();
13432 doForce : function(){
13433 if(this.inputEl().dom.value.length > 0){
13434 this.inputEl().dom.value =
13435 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13441 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13442 * query allowing the query action to be canceled if needed.
13443 * @param {String} query The SQL query to execute
13444 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13445 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13446 * saved in the current store (defaults to false)
13448 doQuery : function(q, forceAll){
13450 if(q === undefined || q === null){
13455 forceAll: forceAll,
13459 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13464 forceAll = qe.forceAll;
13465 if(forceAll === true || (q.length >= this.minChars)){
13467 this.hasQuery = true;
13469 if(this.lastQuery != q || this.alwaysQuery){
13470 this.lastQuery = q;
13471 if(this.mode == 'local'){
13472 this.selectedIndex = -1;
13474 this.store.clearFilter();
13477 if(this.specialFilter){
13478 this.fireEvent('specialfilter', this);
13483 this.store.filter(this.displayField, q);
13486 this.store.fireEvent("datachanged", this.store);
13493 this.store.baseParams[this.queryParam] = q;
13495 var options = {params : this.getParams(q)};
13498 options.add = true;
13499 options.params.start = this.page * this.pageSize;
13502 this.store.load(options);
13505 * this code will make the page width larger, at the beginning, the list not align correctly,
13506 * we should expand the list on onLoad
13507 * so command out it
13512 this.selectedIndex = -1;
13517 this.loadNext = false;
13521 getParams : function(q){
13523 //p[this.queryParam] = q;
13527 p.limit = this.pageSize;
13533 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13535 collapse : function(){
13536 if(!this.isExpanded()){
13543 this.hasFocus = false;
13545 this.cancelBtn.hide();
13546 this.trigger.show();
13549 this.tickableInputEl().dom.value = '';
13550 this.tickableInputEl().blur();
13555 Roo.get(document).un('mousedown', this.collapseIf, this);
13556 Roo.get(document).un('mousewheel', this.collapseIf, this);
13557 if (!this.editable) {
13558 Roo.get(document).un('keydown', this.listKeyPress, this);
13560 this.fireEvent('collapse', this);
13566 collapseIf : function(e){
13567 var in_combo = e.within(this.el);
13568 var in_list = e.within(this.list);
13569 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13571 if (in_combo || in_list || is_list) {
13572 //e.stopPropagation();
13577 this.onTickableFooterButtonClick(e, false, false);
13585 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13587 expand : function(){
13589 if(this.isExpanded() || !this.hasFocus){
13593 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13594 this.list.setWidth(lw);
13601 this.restrictHeight();
13605 this.tickItems = Roo.apply([], this.item);
13608 this.cancelBtn.show();
13609 this.trigger.hide();
13612 this.tickableInputEl().focus();
13617 Roo.get(document).on('mousedown', this.collapseIf, this);
13618 Roo.get(document).on('mousewheel', this.collapseIf, this);
13619 if (!this.editable) {
13620 Roo.get(document).on('keydown', this.listKeyPress, this);
13623 this.fireEvent('expand', this);
13627 // Implements the default empty TriggerField.onTriggerClick function
13628 onTriggerClick : function(e)
13630 Roo.log('trigger click');
13632 if(this.disabled || !this.triggerList){
13637 this.loadNext = false;
13639 if(this.isExpanded()){
13641 if (!this.blockFocus) {
13642 this.inputEl().focus();
13646 this.hasFocus = true;
13647 if(this.triggerAction == 'all') {
13648 this.doQuery(this.allQuery, true);
13650 this.doQuery(this.getRawValue());
13652 if (!this.blockFocus) {
13653 this.inputEl().focus();
13658 onTickableTriggerClick : function(e)
13665 this.loadNext = false;
13666 this.hasFocus = true;
13668 if(this.triggerAction == 'all') {
13669 this.doQuery(this.allQuery, true);
13671 this.doQuery(this.getRawValue());
13675 onSearchFieldClick : function(e)
13677 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13678 this.onTickableFooterButtonClick(e, false, false);
13682 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13687 this.loadNext = false;
13688 this.hasFocus = true;
13690 if(this.triggerAction == 'all') {
13691 this.doQuery(this.allQuery, true);
13693 this.doQuery(this.getRawValue());
13697 listKeyPress : function(e)
13699 //Roo.log('listkeypress');
13700 // scroll to first matching element based on key pres..
13701 if (e.isSpecialKey()) {
13704 var k = String.fromCharCode(e.getKey()).toUpperCase();
13707 var csel = this.view.getSelectedNodes();
13708 var cselitem = false;
13710 var ix = this.view.indexOf(csel[0]);
13711 cselitem = this.store.getAt(ix);
13712 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13718 this.store.each(function(v) {
13720 // start at existing selection.
13721 if (cselitem.id == v.id) {
13727 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13728 match = this.store.indexOf(v);
13734 if (match === false) {
13735 return true; // no more action?
13738 this.view.select(match);
13739 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13740 sn.scrollIntoView(sn.dom.parentNode, false);
13743 onViewScroll : function(e, t){
13745 if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
13749 this.hasQuery = true;
13751 this.loading = this.list.select('.loading', true).first();
13753 if(this.loading === null){
13754 this.list.createChild({
13756 cls: 'loading roo-select2-more-results roo-select2-active',
13757 html: 'Loading more results...'
13760 this.loading = this.list.select('.loading', true).first();
13762 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13764 this.loading.hide();
13767 this.loading.show();
13772 this.loadNext = true;
13774 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13779 addItem : function(o)
13781 var dv = ''; // display value
13783 if (this.displayField) {
13784 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13786 // this is an error condition!!!
13787 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13794 var choice = this.choices.createChild({
13796 cls: 'roo-select2-search-choice',
13805 cls: 'roo-select2-search-choice-close',
13810 }, this.searchField);
13812 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13814 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13822 this.inputEl().dom.value = '';
13827 onRemoveItem : function(e, _self, o)
13829 e.preventDefault();
13831 this.lastItem = Roo.apply([], this.item);
13833 var index = this.item.indexOf(o.data) * 1;
13836 Roo.log('not this item?!');
13840 this.item.splice(index, 1);
13845 this.fireEvent('remove', this, e);
13851 syncValue : function()
13853 if(!this.item.length){
13860 Roo.each(this.item, function(i){
13861 if(_this.valueField){
13862 value.push(i[_this.valueField]);
13869 this.value = value.join(',');
13871 if(this.hiddenField){
13872 this.hiddenField.dom.value = this.value;
13875 this.store.fireEvent("datachanged", this.store);
13880 clearItem : function()
13882 if(!this.multiple){
13888 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13896 if(this.tickable && !Roo.isTouch){
13897 this.view.refresh();
13901 inputEl: function ()
13903 if(Roo.isTouch && this.mobileTouchView){
13904 return this.el.select('input.form-control',true).first();
13908 return this.searchField;
13911 return this.el.select('input.form-control',true).first();
13915 onTickableFooterButtonClick : function(e, btn, el)
13917 e.preventDefault();
13919 this.lastItem = Roo.apply([], this.item);
13921 if(btn && btn.name == 'cancel'){
13922 this.tickItems = Roo.apply([], this.item);
13931 Roo.each(this.tickItems, function(o){
13939 validate : function()
13941 var v = this.getRawValue();
13944 v = this.getValue();
13947 if(this.disabled || this.allowBlank || v.length){
13952 this.markInvalid();
13956 tickableInputEl : function()
13958 if(!this.tickable || !this.editable){
13959 return this.inputEl();
13962 return this.inputEl().select('.roo-select2-search-field-input', true).first();
13966 getAutoCreateTouchView : function()
13971 cls: 'form-group' //input-group
13977 type : this.inputType,
13978 cls : 'form-control x-combo-noedit',
13979 autocomplete: 'new-password',
13980 placeholder : this.placeholder || '',
13985 input.name = this.name;
13989 input.cls += ' input-' + this.size;
13992 if (this.disabled) {
13993 input.disabled = true;
14004 inputblock.cls += ' input-group';
14006 inputblock.cn.unshift({
14008 cls : 'input-group-addon',
14013 if(this.removable && !this.multiple){
14014 inputblock.cls += ' roo-removable';
14016 inputblock.cn.push({
14019 cls : 'roo-combo-removable-btn close'
14023 if(this.hasFeedback && !this.allowBlank){
14025 inputblock.cls += ' has-feedback';
14027 inputblock.cn.push({
14029 cls: 'glyphicon form-control-feedback'
14036 inputblock.cls += (this.before) ? '' : ' input-group';
14038 inputblock.cn.push({
14040 cls : 'input-group-addon',
14051 cls: 'form-hidden-field'
14065 cls: 'form-hidden-field'
14069 cls: 'roo-select2-choices',
14073 cls: 'roo-select2-search-field',
14086 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14092 if(!this.multiple && this.showToggleBtn){
14099 if (this.caret != false) {
14102 cls: 'fa fa-' + this.caret
14109 cls : 'input-group-addon btn dropdown-toggle',
14114 cls: 'combobox-clear',
14128 combobox.cls += ' roo-select2-container-multi';
14131 var align = this.labelAlign || this.parentLabelAlign();
14135 if(this.fieldLabel.length && this.labelWidth){
14137 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14138 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14143 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14144 tooltip : 'This field is required'
14148 cls : 'control-label ' + lw,
14149 html : this.fieldLabel
14160 if(this.indicatorpos == 'right'){
14164 cls : 'control-label ' + lw,
14165 html : this.fieldLabel
14170 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14171 tooltip : 'This field is required'
14183 var settings = this;
14185 ['xs','sm','md','lg'].map(function(size){
14186 if (settings[size]) {
14187 cfg.cls += ' col-' + size + '-' + settings[size];
14194 initTouchView : function()
14196 this.renderTouchView();
14198 this.touchViewEl.on('scroll', function(){
14199 this.el.dom.scrollTop = 0;
14202 this.originalValue = this.getValue();
14204 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14206 this.inputEl().on("click", this.showTouchView, this);
14207 this.triggerEl.on("click", this.showTouchView, this);
14209 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14210 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14212 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14214 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14215 this.store.on('load', this.onTouchViewLoad, this);
14216 this.store.on('loadexception', this.onTouchViewLoadException, this);
14218 if(this.hiddenName){
14220 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14222 this.hiddenField.dom.value =
14223 this.hiddenValue !== undefined ? this.hiddenValue :
14224 this.value !== undefined ? this.value : '';
14226 this.el.dom.removeAttribute('name');
14227 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14231 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14232 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14235 if(this.removable && !this.multiple){
14236 var close = this.closeTriggerEl();
14238 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14239 close.on('click', this.removeBtnClick, this, close);
14243 * fix the bug in Safari iOS8
14245 this.inputEl().on("focus", function(e){
14246 document.activeElement.blur();
14254 renderTouchView : function()
14256 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14257 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14259 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14260 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14262 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14263 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14264 this.touchViewBodyEl.setStyle('overflow', 'auto');
14266 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14267 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14269 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14270 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14274 showTouchView : function()
14280 this.touchViewHeaderEl.hide();
14282 if(this.modalTitle.length){
14283 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14284 this.touchViewHeaderEl.show();
14287 this.touchViewEl.show();
14289 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14290 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14291 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14293 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14295 if(this.modalTitle.length){
14296 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14299 this.touchViewBodyEl.setHeight(bodyHeight);
14303 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14305 this.touchViewEl.addClass('in');
14308 this.doTouchViewQuery();
14312 hideTouchView : function()
14314 this.touchViewEl.removeClass('in');
14318 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14320 this.touchViewEl.setStyle('display', 'none');
14325 setTouchViewValue : function()
14332 Roo.each(this.tickItems, function(o){
14337 this.hideTouchView();
14340 doTouchViewQuery : function()
14349 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14353 if(!this.alwaysQuery || this.mode == 'local'){
14354 this.onTouchViewLoad();
14361 onTouchViewBeforeLoad : function(combo,opts)
14367 onTouchViewLoad : function()
14369 if(this.store.getCount() < 1){
14370 this.onTouchViewEmptyResults();
14374 this.clearTouchView();
14376 var rawValue = this.getRawValue();
14378 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14380 this.tickItems = [];
14382 this.store.data.each(function(d, rowIndex){
14383 var row = this.touchViewListGroup.createChild(template);
14385 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14386 row.addClass(d.data.cls);
14389 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14392 html : d.data[this.displayField]
14395 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14396 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14399 row.removeClass('selected');
14400 if(!this.multiple && this.valueField &&
14401 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14404 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14405 row.addClass('selected');
14408 if(this.multiple && this.valueField &&
14409 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14413 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14414 this.tickItems.push(d.data);
14417 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14421 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14423 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14425 if(this.modalTitle.length){
14426 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14429 var listHeight = this.touchViewListGroup.getHeight();
14433 if(firstChecked && listHeight > bodyHeight){
14434 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14439 onTouchViewLoadException : function()
14441 this.hideTouchView();
14444 onTouchViewEmptyResults : function()
14446 this.clearTouchView();
14448 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14450 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14454 clearTouchView : function()
14456 this.touchViewListGroup.dom.innerHTML = '';
14459 onTouchViewClick : function(e, el, o)
14461 e.preventDefault();
14464 var rowIndex = o.rowIndex;
14466 var r = this.store.getAt(rowIndex);
14468 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14470 if(!this.multiple){
14471 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14472 c.dom.removeAttribute('checked');
14475 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14477 this.setFromData(r.data);
14479 var close = this.closeTriggerEl();
14485 this.hideTouchView();
14487 this.fireEvent('select', this, r, rowIndex);
14492 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14493 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14494 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14498 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14499 this.addItem(r.data);
14500 this.tickItems.push(r.data);
14506 * @cfg {Boolean} grow
14510 * @cfg {Number} growMin
14514 * @cfg {Number} growMax
14523 Roo.apply(Roo.bootstrap.ComboBox, {
14527 cls: 'modal-header',
14549 cls: 'list-group-item',
14553 cls: 'roo-combobox-list-group-item-value'
14557 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14571 listItemCheckbox : {
14573 cls: 'list-group-item',
14577 cls: 'roo-combobox-list-group-item-value'
14581 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14597 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14602 cls: 'modal-footer',
14610 cls: 'col-xs-6 text-left',
14613 cls: 'btn btn-danger roo-touch-view-cancel',
14619 cls: 'col-xs-6 text-right',
14622 cls: 'btn btn-success roo-touch-view-ok',
14633 Roo.apply(Roo.bootstrap.ComboBox, {
14635 touchViewTemplate : {
14637 cls: 'modal fade roo-combobox-touch-view',
14641 cls: 'modal-dialog',
14642 style : 'position:fixed', // we have to fix position....
14646 cls: 'modal-content',
14648 Roo.bootstrap.ComboBox.header,
14649 Roo.bootstrap.ComboBox.body,
14650 Roo.bootstrap.ComboBox.footer
14659 * Ext JS Library 1.1.1
14660 * Copyright(c) 2006-2007, Ext JS, LLC.
14662 * Originally Released Under LGPL - original licence link has changed is not relivant.
14665 * <script type="text/javascript">
14670 * @extends Roo.util.Observable
14671 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14672 * This class also supports single and multi selection modes. <br>
14673 * Create a data model bound view:
14675 var store = new Roo.data.Store(...);
14677 var view = new Roo.View({
14679 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14681 singleSelect: true,
14682 selectedClass: "ydataview-selected",
14686 // listen for node click?
14687 view.on("click", function(vw, index, node, e){
14688 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14692 dataModel.load("foobar.xml");
14694 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14696 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14697 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14699 * Note: old style constructor is still suported (container, template, config)
14702 * Create a new View
14703 * @param {Object} config The config object
14706 Roo.View = function(config, depreciated_tpl, depreciated_config){
14708 this.parent = false;
14710 if (typeof(depreciated_tpl) == 'undefined') {
14711 // new way.. - universal constructor.
14712 Roo.apply(this, config);
14713 this.el = Roo.get(this.el);
14716 this.el = Roo.get(config);
14717 this.tpl = depreciated_tpl;
14718 Roo.apply(this, depreciated_config);
14720 this.wrapEl = this.el.wrap().wrap();
14721 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14724 if(typeof(this.tpl) == "string"){
14725 this.tpl = new Roo.Template(this.tpl);
14727 // support xtype ctors..
14728 this.tpl = new Roo.factory(this.tpl, Roo);
14732 this.tpl.compile();
14737 * @event beforeclick
14738 * Fires before a click is processed. Returns false to cancel the default action.
14739 * @param {Roo.View} this
14740 * @param {Number} index The index of the target node
14741 * @param {HTMLElement} node The target node
14742 * @param {Roo.EventObject} e The raw event object
14744 "beforeclick" : true,
14747 * Fires when a template node is clicked.
14748 * @param {Roo.View} this
14749 * @param {Number} index The index of the target node
14750 * @param {HTMLElement} node The target node
14751 * @param {Roo.EventObject} e The raw event object
14756 * Fires when a template node is double clicked.
14757 * @param {Roo.View} this
14758 * @param {Number} index The index of the target node
14759 * @param {HTMLElement} node The target node
14760 * @param {Roo.EventObject} e The raw event object
14764 * @event contextmenu
14765 * Fires when a template node is right clicked.
14766 * @param {Roo.View} this
14767 * @param {Number} index The index of the target node
14768 * @param {HTMLElement} node The target node
14769 * @param {Roo.EventObject} e The raw event object
14771 "contextmenu" : true,
14773 * @event selectionchange
14774 * Fires when the selected nodes change.
14775 * @param {Roo.View} this
14776 * @param {Array} selections Array of the selected nodes
14778 "selectionchange" : true,
14781 * @event beforeselect
14782 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14783 * @param {Roo.View} this
14784 * @param {HTMLElement} node The node to be selected
14785 * @param {Array} selections Array of currently selected nodes
14787 "beforeselect" : true,
14789 * @event preparedata
14790 * Fires on every row to render, to allow you to change the data.
14791 * @param {Roo.View} this
14792 * @param {Object} data to be rendered (change this)
14794 "preparedata" : true
14802 "click": this.onClick,
14803 "dblclick": this.onDblClick,
14804 "contextmenu": this.onContextMenu,
14808 this.selections = [];
14810 this.cmp = new Roo.CompositeElementLite([]);
14812 this.store = Roo.factory(this.store, Roo.data);
14813 this.setStore(this.store, true);
14816 if ( this.footer && this.footer.xtype) {
14818 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14820 this.footer.dataSource = this.store;
14821 this.footer.container = fctr;
14822 this.footer = Roo.factory(this.footer, Roo);
14823 fctr.insertFirst(this.el);
14825 // this is a bit insane - as the paging toolbar seems to detach the el..
14826 // dom.parentNode.parentNode.parentNode
14827 // they get detached?
14831 Roo.View.superclass.constructor.call(this);
14836 Roo.extend(Roo.View, Roo.util.Observable, {
14839 * @cfg {Roo.data.Store} store Data store to load data from.
14844 * @cfg {String|Roo.Element} el The container element.
14849 * @cfg {String|Roo.Template} tpl The template used by this View
14853 * @cfg {String} dataName the named area of the template to use as the data area
14854 * Works with domtemplates roo-name="name"
14858 * @cfg {String} selectedClass The css class to add to selected nodes
14860 selectedClass : "x-view-selected",
14862 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14867 * @cfg {String} text to display on mask (default Loading)
14871 * @cfg {Boolean} multiSelect Allow multiple selection
14873 multiSelect : false,
14875 * @cfg {Boolean} singleSelect Allow single selection
14877 singleSelect: false,
14880 * @cfg {Boolean} toggleSelect - selecting
14882 toggleSelect : false,
14885 * @cfg {Boolean} tickable - selecting
14890 * Returns the element this view is bound to.
14891 * @return {Roo.Element}
14893 getEl : function(){
14894 return this.wrapEl;
14900 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14902 refresh : function(){
14903 //Roo.log('refresh');
14906 // if we are using something like 'domtemplate', then
14907 // the what gets used is:
14908 // t.applySubtemplate(NAME, data, wrapping data..)
14909 // the outer template then get' applied with
14910 // the store 'extra data'
14911 // and the body get's added to the
14912 // roo-name="data" node?
14913 // <span class='roo-tpl-{name}'></span> ?????
14917 this.clearSelections();
14918 this.el.update("");
14920 var records = this.store.getRange();
14921 if(records.length < 1) {
14923 // is this valid?? = should it render a template??
14925 this.el.update(this.emptyText);
14929 if (this.dataName) {
14930 this.el.update(t.apply(this.store.meta)); //????
14931 el = this.el.child('.roo-tpl-' + this.dataName);
14934 for(var i = 0, len = records.length; i < len; i++){
14935 var data = this.prepareData(records[i].data, i, records[i]);
14936 this.fireEvent("preparedata", this, data, i, records[i]);
14938 var d = Roo.apply({}, data);
14941 Roo.apply(d, {'roo-id' : Roo.id()});
14945 Roo.each(this.parent.item, function(item){
14946 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14949 Roo.apply(d, {'roo-data-checked' : 'checked'});
14953 html[html.length] = Roo.util.Format.trim(
14955 t.applySubtemplate(this.dataName, d, this.store.meta) :
14962 el.update(html.join(""));
14963 this.nodes = el.dom.childNodes;
14964 this.updateIndexes(0);
14969 * Function to override to reformat the data that is sent to
14970 * the template for each node.
14971 * DEPRICATED - use the preparedata event handler.
14972 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14973 * a JSON object for an UpdateManager bound view).
14975 prepareData : function(data, index, record)
14977 this.fireEvent("preparedata", this, data, index, record);
14981 onUpdate : function(ds, record){
14982 // Roo.log('on update');
14983 this.clearSelections();
14984 var index = this.store.indexOf(record);
14985 var n = this.nodes[index];
14986 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14987 n.parentNode.removeChild(n);
14988 this.updateIndexes(index, index);
14994 onAdd : function(ds, records, index)
14996 //Roo.log(['on Add', ds, records, index] );
14997 this.clearSelections();
14998 if(this.nodes.length == 0){
15002 var n = this.nodes[index];
15003 for(var i = 0, len = records.length; i < len; i++){
15004 var d = this.prepareData(records[i].data, i, records[i]);
15006 this.tpl.insertBefore(n, d);
15009 this.tpl.append(this.el, d);
15012 this.updateIndexes(index);
15015 onRemove : function(ds, record, index){
15016 // Roo.log('onRemove');
15017 this.clearSelections();
15018 var el = this.dataName ?
15019 this.el.child('.roo-tpl-' + this.dataName) :
15022 el.dom.removeChild(this.nodes[index]);
15023 this.updateIndexes(index);
15027 * Refresh an individual node.
15028 * @param {Number} index
15030 refreshNode : function(index){
15031 this.onUpdate(this.store, this.store.getAt(index));
15034 updateIndexes : function(startIndex, endIndex){
15035 var ns = this.nodes;
15036 startIndex = startIndex || 0;
15037 endIndex = endIndex || ns.length - 1;
15038 for(var i = startIndex; i <= endIndex; i++){
15039 ns[i].nodeIndex = i;
15044 * Changes the data store this view uses and refresh the view.
15045 * @param {Store} store
15047 setStore : function(store, initial){
15048 if(!initial && this.store){
15049 this.store.un("datachanged", this.refresh);
15050 this.store.un("add", this.onAdd);
15051 this.store.un("remove", this.onRemove);
15052 this.store.un("update", this.onUpdate);
15053 this.store.un("clear", this.refresh);
15054 this.store.un("beforeload", this.onBeforeLoad);
15055 this.store.un("load", this.onLoad);
15056 this.store.un("loadexception", this.onLoad);
15060 store.on("datachanged", this.refresh, this);
15061 store.on("add", this.onAdd, this);
15062 store.on("remove", this.onRemove, this);
15063 store.on("update", this.onUpdate, this);
15064 store.on("clear", this.refresh, this);
15065 store.on("beforeload", this.onBeforeLoad, this);
15066 store.on("load", this.onLoad, this);
15067 store.on("loadexception", this.onLoad, this);
15075 * onbeforeLoad - masks the loading area.
15078 onBeforeLoad : function(store,opts)
15080 //Roo.log('onBeforeLoad');
15082 this.el.update("");
15084 this.el.mask(this.mask ? this.mask : "Loading" );
15086 onLoad : function ()
15093 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15094 * @param {HTMLElement} node
15095 * @return {HTMLElement} The template node
15097 findItemFromChild : function(node){
15098 var el = this.dataName ?
15099 this.el.child('.roo-tpl-' + this.dataName,true) :
15102 if(!node || node.parentNode == el){
15105 var p = node.parentNode;
15106 while(p && p != el){
15107 if(p.parentNode == el){
15116 onClick : function(e){
15117 var item = this.findItemFromChild(e.getTarget());
15119 var index = this.indexOf(item);
15120 if(this.onItemClick(item, index, e) !== false){
15121 this.fireEvent("click", this, index, item, e);
15124 this.clearSelections();
15129 onContextMenu : function(e){
15130 var item = this.findItemFromChild(e.getTarget());
15132 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15137 onDblClick : function(e){
15138 var item = this.findItemFromChild(e.getTarget());
15140 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15144 onItemClick : function(item, index, e)
15146 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15149 if (this.toggleSelect) {
15150 var m = this.isSelected(item) ? 'unselect' : 'select';
15153 _t[m](item, true, false);
15156 if(this.multiSelect || this.singleSelect){
15157 if(this.multiSelect && e.shiftKey && this.lastSelection){
15158 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15160 this.select(item, this.multiSelect && e.ctrlKey);
15161 this.lastSelection = item;
15164 if(!this.tickable){
15165 e.preventDefault();
15173 * Get the number of selected nodes.
15176 getSelectionCount : function(){
15177 return this.selections.length;
15181 * Get the currently selected nodes.
15182 * @return {Array} An array of HTMLElements
15184 getSelectedNodes : function(){
15185 return this.selections;
15189 * Get the indexes of the selected nodes.
15192 getSelectedIndexes : function(){
15193 var indexes = [], s = this.selections;
15194 for(var i = 0, len = s.length; i < len; i++){
15195 indexes.push(s[i].nodeIndex);
15201 * Clear all selections
15202 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15204 clearSelections : function(suppressEvent){
15205 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15206 this.cmp.elements = this.selections;
15207 this.cmp.removeClass(this.selectedClass);
15208 this.selections = [];
15209 if(!suppressEvent){
15210 this.fireEvent("selectionchange", this, this.selections);
15216 * Returns true if the passed node is selected
15217 * @param {HTMLElement/Number} node The node or node index
15218 * @return {Boolean}
15220 isSelected : function(node){
15221 var s = this.selections;
15225 node = this.getNode(node);
15226 return s.indexOf(node) !== -1;
15231 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
15232 * @param {Boolean} keepExisting (optional) true to keep existing selections
15233 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15235 select : function(nodeInfo, keepExisting, suppressEvent){
15236 if(nodeInfo instanceof Array){
15238 this.clearSelections(true);
15240 for(var i = 0, len = nodeInfo.length; i < len; i++){
15241 this.select(nodeInfo[i], true, true);
15245 var node = this.getNode(nodeInfo);
15246 if(!node || this.isSelected(node)){
15247 return; // already selected.
15250 this.clearSelections(true);
15253 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15254 Roo.fly(node).addClass(this.selectedClass);
15255 this.selections.push(node);
15256 if(!suppressEvent){
15257 this.fireEvent("selectionchange", this, this.selections);
15265 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
15266 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15267 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15269 unselect : function(nodeInfo, keepExisting, suppressEvent)
15271 if(nodeInfo instanceof Array){
15272 Roo.each(this.selections, function(s) {
15273 this.unselect(s, nodeInfo);
15277 var node = this.getNode(nodeInfo);
15278 if(!node || !this.isSelected(node)){
15279 //Roo.log("not selected");
15280 return; // not selected.
15284 Roo.each(this.selections, function(s) {
15286 Roo.fly(node).removeClass(this.selectedClass);
15293 this.selections= ns;
15294 this.fireEvent("selectionchange", this, this.selections);
15298 * Gets a template node.
15299 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15300 * @return {HTMLElement} The node or null if it wasn't found
15302 getNode : function(nodeInfo){
15303 if(typeof nodeInfo == "string"){
15304 return document.getElementById(nodeInfo);
15305 }else if(typeof nodeInfo == "number"){
15306 return this.nodes[nodeInfo];
15312 * Gets a range template nodes.
15313 * @param {Number} startIndex
15314 * @param {Number} endIndex
15315 * @return {Array} An array of nodes
15317 getNodes : function(start, end){
15318 var ns = this.nodes;
15319 start = start || 0;
15320 end = typeof end == "undefined" ? ns.length - 1 : end;
15323 for(var i = start; i <= end; i++){
15327 for(var i = start; i >= end; i--){
15335 * Finds the index of the passed node
15336 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15337 * @return {Number} The index of the node or -1
15339 indexOf : function(node){
15340 node = this.getNode(node);
15341 if(typeof node.nodeIndex == "number"){
15342 return node.nodeIndex;
15344 var ns = this.nodes;
15345 for(var i = 0, len = ns.length; i < len; i++){
15356 * based on jquery fullcalendar
15360 Roo.bootstrap = Roo.bootstrap || {};
15362 * @class Roo.bootstrap.Calendar
15363 * @extends Roo.bootstrap.Component
15364 * Bootstrap Calendar class
15365 * @cfg {Boolean} loadMask (true|false) default false
15366 * @cfg {Object} header generate the user specific header of the calendar, default false
15369 * Create a new Container
15370 * @param {Object} config The config object
15375 Roo.bootstrap.Calendar = function(config){
15376 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15380 * Fires when a date is selected
15381 * @param {DatePicker} this
15382 * @param {Date} date The selected date
15386 * @event monthchange
15387 * Fires when the displayed month changes
15388 * @param {DatePicker} this
15389 * @param {Date} date The selected month
15391 'monthchange': true,
15393 * @event evententer
15394 * Fires when mouse over an event
15395 * @param {Calendar} this
15396 * @param {event} Event
15398 'evententer': true,
15400 * @event eventleave
15401 * Fires when the mouse leaves an
15402 * @param {Calendar} this
15405 'eventleave': true,
15407 * @event eventclick
15408 * Fires when the mouse click an
15409 * @param {Calendar} this
15418 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15421 * @cfg {Number} startDay
15422 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15430 getAutoCreate : function(){
15433 var fc_button = function(name, corner, style, content ) {
15434 return Roo.apply({},{
15436 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15438 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15441 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15452 style : 'width:100%',
15459 cls : 'fc-header-left',
15461 fc_button('prev', 'left', 'arrow', '‹' ),
15462 fc_button('next', 'right', 'arrow', '›' ),
15463 { tag: 'span', cls: 'fc-header-space' },
15464 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15472 cls : 'fc-header-center',
15476 cls: 'fc-header-title',
15479 html : 'month / year'
15487 cls : 'fc-header-right',
15489 /* fc_button('month', 'left', '', 'month' ),
15490 fc_button('week', '', '', 'week' ),
15491 fc_button('day', 'right', '', 'day' )
15503 header = this.header;
15506 var cal_heads = function() {
15508 // fixme - handle this.
15510 for (var i =0; i < Date.dayNames.length; i++) {
15511 var d = Date.dayNames[i];
15514 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15515 html : d.substring(0,3)
15519 ret[0].cls += ' fc-first';
15520 ret[6].cls += ' fc-last';
15523 var cal_cell = function(n) {
15526 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15531 cls: 'fc-day-number',
15535 cls: 'fc-day-content',
15539 style: 'position: relative;' // height: 17px;
15551 var cal_rows = function() {
15554 for (var r = 0; r < 6; r++) {
15561 for (var i =0; i < Date.dayNames.length; i++) {
15562 var d = Date.dayNames[i];
15563 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15566 row.cn[0].cls+=' fc-first';
15567 row.cn[0].cn[0].style = 'min-height:90px';
15568 row.cn[6].cls+=' fc-last';
15572 ret[0].cls += ' fc-first';
15573 ret[4].cls += ' fc-prev-last';
15574 ret[5].cls += ' fc-last';
15581 cls: 'fc-border-separate',
15582 style : 'width:100%',
15590 cls : 'fc-first fc-last',
15608 cls : 'fc-content',
15609 style : "position: relative;",
15612 cls : 'fc-view fc-view-month fc-grid',
15613 style : 'position: relative',
15614 unselectable : 'on',
15617 cls : 'fc-event-container',
15618 style : 'position:absolute;z-index:8;top:0;left:0;'
15636 initEvents : function()
15639 throw "can not find store for calendar";
15645 style: "text-align:center",
15649 style: "background-color:white;width:50%;margin:250 auto",
15653 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15664 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15666 var size = this.el.select('.fc-content', true).first().getSize();
15667 this.maskEl.setSize(size.width, size.height);
15668 this.maskEl.enableDisplayMode("block");
15669 if(!this.loadMask){
15670 this.maskEl.hide();
15673 this.store = Roo.factory(this.store, Roo.data);
15674 this.store.on('load', this.onLoad, this);
15675 this.store.on('beforeload', this.onBeforeLoad, this);
15679 this.cells = this.el.select('.fc-day',true);
15680 //Roo.log(this.cells);
15681 this.textNodes = this.el.query('.fc-day-number');
15682 this.cells.addClassOnOver('fc-state-hover');
15684 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15685 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15686 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15687 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15689 this.on('monthchange', this.onMonthChange, this);
15691 this.update(new Date().clearTime());
15694 resize : function() {
15695 var sz = this.el.getSize();
15697 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15698 this.el.select('.fc-day-content div',true).setHeight(34);
15703 showPrevMonth : function(e){
15704 this.update(this.activeDate.add("mo", -1));
15706 showToday : function(e){
15707 this.update(new Date().clearTime());
15710 showNextMonth : function(e){
15711 this.update(this.activeDate.add("mo", 1));
15715 showPrevYear : function(){
15716 this.update(this.activeDate.add("y", -1));
15720 showNextYear : function(){
15721 this.update(this.activeDate.add("y", 1));
15726 update : function(date)
15728 var vd = this.activeDate;
15729 this.activeDate = date;
15730 // if(vd && this.el){
15731 // var t = date.getTime();
15732 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15733 // Roo.log('using add remove');
15735 // this.fireEvent('monthchange', this, date);
15737 // this.cells.removeClass("fc-state-highlight");
15738 // this.cells.each(function(c){
15739 // if(c.dateValue == t){
15740 // c.addClass("fc-state-highlight");
15741 // setTimeout(function(){
15742 // try{c.dom.firstChild.focus();}catch(e){}
15752 var days = date.getDaysInMonth();
15754 var firstOfMonth = date.getFirstDateOfMonth();
15755 var startingPos = firstOfMonth.getDay()-this.startDay;
15757 if(startingPos < this.startDay){
15761 var pm = date.add(Date.MONTH, -1);
15762 var prevStart = pm.getDaysInMonth()-startingPos;
15764 this.cells = this.el.select('.fc-day',true);
15765 this.textNodes = this.el.query('.fc-day-number');
15766 this.cells.addClassOnOver('fc-state-hover');
15768 var cells = this.cells.elements;
15769 var textEls = this.textNodes;
15771 Roo.each(cells, function(cell){
15772 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15775 days += startingPos;
15777 // convert everything to numbers so it's fast
15778 var day = 86400000;
15779 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15782 //Roo.log(prevStart);
15784 var today = new Date().clearTime().getTime();
15785 var sel = date.clearTime().getTime();
15786 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15787 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15788 var ddMatch = this.disabledDatesRE;
15789 var ddText = this.disabledDatesText;
15790 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15791 var ddaysText = this.disabledDaysText;
15792 var format = this.format;
15794 var setCellClass = function(cal, cell){
15798 //Roo.log('set Cell Class');
15800 var t = d.getTime();
15804 cell.dateValue = t;
15806 cell.className += " fc-today";
15807 cell.className += " fc-state-highlight";
15808 cell.title = cal.todayText;
15811 // disable highlight in other month..
15812 //cell.className += " fc-state-highlight";
15817 cell.className = " fc-state-disabled";
15818 cell.title = cal.minText;
15822 cell.className = " fc-state-disabled";
15823 cell.title = cal.maxText;
15827 if(ddays.indexOf(d.getDay()) != -1){
15828 cell.title = ddaysText;
15829 cell.className = " fc-state-disabled";
15832 if(ddMatch && format){
15833 var fvalue = d.dateFormat(format);
15834 if(ddMatch.test(fvalue)){
15835 cell.title = ddText.replace("%0", fvalue);
15836 cell.className = " fc-state-disabled";
15840 if (!cell.initialClassName) {
15841 cell.initialClassName = cell.dom.className;
15844 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15849 for(; i < startingPos; i++) {
15850 textEls[i].innerHTML = (++prevStart);
15851 d.setDate(d.getDate()+1);
15853 cells[i].className = "fc-past fc-other-month";
15854 setCellClass(this, cells[i]);
15859 for(; i < days; i++){
15860 intDay = i - startingPos + 1;
15861 textEls[i].innerHTML = (intDay);
15862 d.setDate(d.getDate()+1);
15864 cells[i].className = ''; // "x-date-active";
15865 setCellClass(this, cells[i]);
15869 for(; i < 42; i++) {
15870 textEls[i].innerHTML = (++extraDays);
15871 d.setDate(d.getDate()+1);
15873 cells[i].className = "fc-future fc-other-month";
15874 setCellClass(this, cells[i]);
15877 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15879 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15881 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15882 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15884 if(totalRows != 6){
15885 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15886 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15889 this.fireEvent('monthchange', this, date);
15893 if(!this.internalRender){
15894 var main = this.el.dom.firstChild;
15895 var w = main.offsetWidth;
15896 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15897 Roo.fly(main).setWidth(w);
15898 this.internalRender = true;
15899 // opera does not respect the auto grow header center column
15900 // then, after it gets a width opera refuses to recalculate
15901 // without a second pass
15902 if(Roo.isOpera && !this.secondPass){
15903 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15904 this.secondPass = true;
15905 this.update.defer(10, this, [date]);
15912 findCell : function(dt) {
15913 dt = dt.clearTime().getTime();
15915 this.cells.each(function(c){
15916 //Roo.log("check " +c.dateValue + '?=' + dt);
15917 if(c.dateValue == dt){
15927 findCells : function(ev) {
15928 var s = ev.start.clone().clearTime().getTime();
15930 var e= ev.end.clone().clearTime().getTime();
15933 this.cells.each(function(c){
15934 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15936 if(c.dateValue > e){
15939 if(c.dateValue < s){
15948 // findBestRow: function(cells)
15952 // for (var i =0 ; i < cells.length;i++) {
15953 // ret = Math.max(cells[i].rows || 0,ret);
15960 addItem : function(ev)
15962 // look for vertical location slot in
15963 var cells = this.findCells(ev);
15965 // ev.row = this.findBestRow(cells);
15967 // work out the location.
15971 for(var i =0; i < cells.length; i++) {
15973 cells[i].row = cells[0].row;
15976 cells[i].row = cells[i].row + 1;
15986 if (crow.start.getY() == cells[i].getY()) {
15988 crow.end = cells[i];
16005 cells[0].events.push(ev);
16007 this.calevents.push(ev);
16010 clearEvents: function() {
16012 if(!this.calevents){
16016 Roo.each(this.cells.elements, function(c){
16022 Roo.each(this.calevents, function(e) {
16023 Roo.each(e.els, function(el) {
16024 el.un('mouseenter' ,this.onEventEnter, this);
16025 el.un('mouseleave' ,this.onEventLeave, this);
16030 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16036 renderEvents: function()
16040 this.cells.each(function(c) {
16049 if(c.row != c.events.length){
16050 r = 4 - (4 - (c.row - c.events.length));
16053 c.events = ev.slice(0, r);
16054 c.more = ev.slice(r);
16056 if(c.more.length && c.more.length == 1){
16057 c.events.push(c.more.pop());
16060 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16064 this.cells.each(function(c) {
16066 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16069 for (var e = 0; e < c.events.length; e++){
16070 var ev = c.events[e];
16071 var rows = ev.rows;
16073 for(var i = 0; i < rows.length; i++) {
16075 // how many rows should it span..
16078 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16079 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16081 unselectable : "on",
16084 cls: 'fc-event-inner',
16088 // cls: 'fc-event-time',
16089 // html : cells.length > 1 ? '' : ev.time
16093 cls: 'fc-event-title',
16094 html : String.format('{0}', ev.title)
16101 cls: 'ui-resizable-handle ui-resizable-e',
16102 html : '  '
16109 cfg.cls += ' fc-event-start';
16111 if ((i+1) == rows.length) {
16112 cfg.cls += ' fc-event-end';
16115 var ctr = _this.el.select('.fc-event-container',true).first();
16116 var cg = ctr.createChild(cfg);
16118 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16119 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16121 var r = (c.more.length) ? 1 : 0;
16122 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16123 cg.setWidth(ebox.right - sbox.x -2);
16125 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16126 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16127 cg.on('click', _this.onEventClick, _this, ev);
16138 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16139 style : 'position: absolute',
16140 unselectable : "on",
16143 cls: 'fc-event-inner',
16147 cls: 'fc-event-title',
16155 cls: 'ui-resizable-handle ui-resizable-e',
16156 html : '  '
16162 var ctr = _this.el.select('.fc-event-container',true).first();
16163 var cg = ctr.createChild(cfg);
16165 var sbox = c.select('.fc-day-content',true).first().getBox();
16166 var ebox = c.select('.fc-day-content',true).first().getBox();
16168 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16169 cg.setWidth(ebox.right - sbox.x -2);
16171 cg.on('click', _this.onMoreEventClick, _this, c.more);
16181 onEventEnter: function (e, el,event,d) {
16182 this.fireEvent('evententer', this, el, event);
16185 onEventLeave: function (e, el,event,d) {
16186 this.fireEvent('eventleave', this, el, event);
16189 onEventClick: function (e, el,event,d) {
16190 this.fireEvent('eventclick', this, el, event);
16193 onMonthChange: function () {
16197 onMoreEventClick: function(e, el, more)
16201 this.calpopover.placement = 'right';
16202 this.calpopover.setTitle('More');
16204 this.calpopover.setContent('');
16206 var ctr = this.calpopover.el.select('.popover-content', true).first();
16208 Roo.each(more, function(m){
16210 cls : 'fc-event-hori fc-event-draggable',
16213 var cg = ctr.createChild(cfg);
16215 cg.on('click', _this.onEventClick, _this, m);
16218 this.calpopover.show(el);
16223 onLoad: function ()
16225 this.calevents = [];
16228 if(this.store.getCount() > 0){
16229 this.store.data.each(function(d){
16232 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16233 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16234 time : d.data.start_time,
16235 title : d.data.title,
16236 description : d.data.description,
16237 venue : d.data.venue
16242 this.renderEvents();
16244 if(this.calevents.length && this.loadMask){
16245 this.maskEl.hide();
16249 onBeforeLoad: function()
16251 this.clearEvents();
16253 this.maskEl.show();
16267 * @class Roo.bootstrap.Popover
16268 * @extends Roo.bootstrap.Component
16269 * Bootstrap Popover class
16270 * @cfg {String} html contents of the popover (or false to use children..)
16271 * @cfg {String} title of popover (or false to hide)
16272 * @cfg {String} placement how it is placed
16273 * @cfg {String} trigger click || hover (or false to trigger manually)
16274 * @cfg {String} over what (parent or false to trigger manually.)
16275 * @cfg {Number} delay - delay before showing
16278 * Create a new Popover
16279 * @param {Object} config The config object
16282 Roo.bootstrap.Popover = function(config){
16283 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16289 * After the popover show
16291 * @param {Roo.bootstrap.Popover} this
16296 * After the popover hide
16298 * @param {Roo.bootstrap.Popover} this
16304 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16306 title: 'Fill in a title',
16309 placement : 'right',
16310 trigger : 'hover', // hover
16316 can_build_overlaid : false,
16318 getChildContainer : function()
16320 return this.el.select('.popover-content',true).first();
16323 getAutoCreate : function(){
16326 cls : 'popover roo-dynamic',
16327 style: 'display:block',
16333 cls : 'popover-inner',
16337 cls: 'popover-title',
16341 cls : 'popover-content',
16352 setTitle: function(str)
16355 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16357 setContent: function(str)
16360 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16362 // as it get's added to the bottom of the page.
16363 onRender : function(ct, position)
16365 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16367 var cfg = Roo.apply({}, this.getAutoCreate());
16371 cfg.cls += ' ' + this.cls;
16374 cfg.style = this.style;
16376 //Roo.log("adding to ");
16377 this.el = Roo.get(document.body).createChild(cfg, position);
16378 // Roo.log(this.el);
16383 initEvents : function()
16385 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16386 this.el.enableDisplayMode('block');
16388 if (this.over === false) {
16391 if (this.triggers === false) {
16394 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16395 var triggers = this.trigger ? this.trigger.split(' ') : [];
16396 Roo.each(triggers, function(trigger) {
16398 if (trigger == 'click') {
16399 on_el.on('click', this.toggle, this);
16400 } else if (trigger != 'manual') {
16401 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16402 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16404 on_el.on(eventIn ,this.enter, this);
16405 on_el.on(eventOut, this.leave, this);
16416 toggle : function () {
16417 this.hoverState == 'in' ? this.leave() : this.enter();
16420 enter : function () {
16422 clearTimeout(this.timeout);
16424 this.hoverState = 'in';
16426 if (!this.delay || !this.delay.show) {
16431 this.timeout = setTimeout(function () {
16432 if (_t.hoverState == 'in') {
16435 }, this.delay.show)
16438 leave : function() {
16439 clearTimeout(this.timeout);
16441 this.hoverState = 'out';
16443 if (!this.delay || !this.delay.hide) {
16448 this.timeout = setTimeout(function () {
16449 if (_t.hoverState == 'out') {
16452 }, this.delay.hide)
16455 show : function (on_el)
16458 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16462 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16463 if (this.html !== false) {
16464 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16466 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16467 if (!this.title.length) {
16468 this.el.select('.popover-title',true).hide();
16471 var placement = typeof this.placement == 'function' ?
16472 this.placement.call(this, this.el, on_el) :
16475 var autoToken = /\s?auto?\s?/i;
16476 var autoPlace = autoToken.test(placement);
16478 placement = placement.replace(autoToken, '') || 'top';
16482 //this.el.setXY([0,0]);
16484 this.el.dom.style.display='block';
16485 this.el.addClass(placement);
16487 //this.el.appendTo(on_el);
16489 var p = this.getPosition();
16490 var box = this.el.getBox();
16495 var align = Roo.bootstrap.Popover.alignment[placement];
16496 this.el.alignTo(on_el, align[0],align[1]);
16497 //var arrow = this.el.select('.arrow',true).first();
16498 //arrow.set(align[2],
16500 this.el.addClass('in');
16503 if (this.el.hasClass('fade')) {
16507 this.hoverState = 'in';
16509 this.fireEvent('show', this);
16514 this.el.setXY([0,0]);
16515 this.el.removeClass('in');
16517 this.hoverState = null;
16519 this.fireEvent('hide', this);
16524 Roo.bootstrap.Popover.alignment = {
16525 'left' : ['r-l', [-10,0], 'right'],
16526 'right' : ['l-r', [10,0], 'left'],
16527 'bottom' : ['t-b', [0,10], 'top'],
16528 'top' : [ 'b-t', [0,-10], 'bottom']
16539 * @class Roo.bootstrap.Progress
16540 * @extends Roo.bootstrap.Component
16541 * Bootstrap Progress class
16542 * @cfg {Boolean} striped striped of the progress bar
16543 * @cfg {Boolean} active animated of the progress bar
16547 * Create a new Progress
16548 * @param {Object} config The config object
16551 Roo.bootstrap.Progress = function(config){
16552 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16555 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16560 getAutoCreate : function(){
16568 cfg.cls += ' progress-striped';
16572 cfg.cls += ' active';
16591 * @class Roo.bootstrap.ProgressBar
16592 * @extends Roo.bootstrap.Component
16593 * Bootstrap ProgressBar class
16594 * @cfg {Number} aria_valuenow aria-value now
16595 * @cfg {Number} aria_valuemin aria-value min
16596 * @cfg {Number} aria_valuemax aria-value max
16597 * @cfg {String} label label for the progress bar
16598 * @cfg {String} panel (success | info | warning | danger )
16599 * @cfg {String} role role of the progress bar
16600 * @cfg {String} sr_only text
16604 * Create a new ProgressBar
16605 * @param {Object} config The config object
16608 Roo.bootstrap.ProgressBar = function(config){
16609 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16612 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16616 aria_valuemax : 100,
16622 getAutoCreate : function()
16627 cls: 'progress-bar',
16628 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16640 cfg.role = this.role;
16643 if(this.aria_valuenow){
16644 cfg['aria-valuenow'] = this.aria_valuenow;
16647 if(this.aria_valuemin){
16648 cfg['aria-valuemin'] = this.aria_valuemin;
16651 if(this.aria_valuemax){
16652 cfg['aria-valuemax'] = this.aria_valuemax;
16655 if(this.label && !this.sr_only){
16656 cfg.html = this.label;
16660 cfg.cls += ' progress-bar-' + this.panel;
16666 update : function(aria_valuenow)
16668 this.aria_valuenow = aria_valuenow;
16670 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16685 * @class Roo.bootstrap.TabGroup
16686 * @extends Roo.bootstrap.Column
16687 * Bootstrap Column class
16688 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16689 * @cfg {Boolean} carousel true to make the group behave like a carousel
16690 * @cfg {Boolean} bullets show bullets for the panels
16691 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16692 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16693 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16694 * @cfg {Boolean} showarrow (true|false) show arrow default true
16697 * Create a new TabGroup
16698 * @param {Object} config The config object
16701 Roo.bootstrap.TabGroup = function(config){
16702 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16704 this.navId = Roo.id();
16707 Roo.bootstrap.TabGroup.register(this);
16711 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16714 transition : false,
16719 slideOnTouch : false,
16722 getAutoCreate : function()
16724 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16726 cfg.cls += ' tab-content';
16728 if (this.carousel) {
16729 cfg.cls += ' carousel slide';
16732 cls : 'carousel-inner',
16736 if(this.bullets && !Roo.isTouch){
16739 cls : 'carousel-bullets',
16743 if(this.bullets_cls){
16744 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16751 cfg.cn[0].cn.push(bullets);
16754 if(this.showarrow){
16755 cfg.cn[0].cn.push({
16757 class : 'carousel-arrow',
16761 class : 'carousel-prev',
16765 class : 'fa fa-chevron-left'
16771 class : 'carousel-next',
16775 class : 'fa fa-chevron-right'
16788 initEvents: function()
16790 if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16791 this.el.on("touchstart", this.onTouchStart, this);
16794 if(this.autoslide){
16797 this.slideFn = window.setInterval(function() {
16798 _this.showPanelNext();
16802 if(this.showarrow){
16803 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16804 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16810 onTouchStart : function(e, el, o)
16812 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16816 this.showPanelNext();
16819 getChildContainer : function()
16821 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16825 * register a Navigation item
16826 * @param {Roo.bootstrap.NavItem} the navitem to add
16828 register : function(item)
16830 this.tabs.push( item);
16831 item.navId = this.navId; // not really needed..
16836 getActivePanel : function()
16839 Roo.each(this.tabs, function(t) {
16849 getPanelByName : function(n)
16852 Roo.each(this.tabs, function(t) {
16853 if (t.tabId == n) {
16861 indexOfPanel : function(p)
16864 Roo.each(this.tabs, function(t,i) {
16865 if (t.tabId == p.tabId) {
16874 * show a specific panel
16875 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16876 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16878 showPanel : function (pan)
16880 if(this.transition || typeof(pan) == 'undefined'){
16881 Roo.log("waiting for the transitionend");
16885 if (typeof(pan) == 'number') {
16886 pan = this.tabs[pan];
16889 if (typeof(pan) == 'string') {
16890 pan = this.getPanelByName(pan);
16893 var cur = this.getActivePanel();
16896 Roo.log('pan or acitve pan is undefined');
16900 if (pan.tabId == this.getActivePanel().tabId) {
16904 if (false === cur.fireEvent('beforedeactivate')) {
16908 if(this.bullets > 0 && !Roo.isTouch){
16909 this.setActiveBullet(this.indexOfPanel(pan));
16912 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16914 this.transition = true;
16915 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16916 var lr = dir == 'next' ? 'left' : 'right';
16917 pan.el.addClass(dir); // or prev
16918 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16919 cur.el.addClass(lr); // or right
16920 pan.el.addClass(lr);
16923 cur.el.on('transitionend', function() {
16924 Roo.log("trans end?");
16926 pan.el.removeClass([lr,dir]);
16927 pan.setActive(true);
16929 cur.el.removeClass([lr]);
16930 cur.setActive(false);
16932 _this.transition = false;
16934 }, this, { single: true } );
16939 cur.setActive(false);
16940 pan.setActive(true);
16945 showPanelNext : function()
16947 var i = this.indexOfPanel(this.getActivePanel());
16949 if (i >= this.tabs.length - 1 && !this.autoslide) {
16953 if (i >= this.tabs.length - 1 && this.autoslide) {
16957 this.showPanel(this.tabs[i+1]);
16960 showPanelPrev : function()
16962 var i = this.indexOfPanel(this.getActivePanel());
16964 if (i < 1 && !this.autoslide) {
16968 if (i < 1 && this.autoslide) {
16969 i = this.tabs.length;
16972 this.showPanel(this.tabs[i-1]);
16976 addBullet: function()
16978 if(!this.bullets || Roo.isTouch){
16981 var ctr = this.el.select('.carousel-bullets',true).first();
16982 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16983 var bullet = ctr.createChild({
16984 cls : 'bullet bullet-' + i
16985 },ctr.dom.lastChild);
16990 bullet.on('click', (function(e, el, o, ii, t){
16992 e.preventDefault();
16994 this.showPanel(ii);
16996 if(this.autoslide && this.slideFn){
16997 clearInterval(this.slideFn);
16998 this.slideFn = window.setInterval(function() {
16999 _this.showPanelNext();
17003 }).createDelegate(this, [i, bullet], true));
17008 setActiveBullet : function(i)
17014 Roo.each(this.el.select('.bullet', true).elements, function(el){
17015 el.removeClass('selected');
17018 var bullet = this.el.select('.bullet-' + i, true).first();
17024 bullet.addClass('selected');
17035 Roo.apply(Roo.bootstrap.TabGroup, {
17039 * register a Navigation Group
17040 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17042 register : function(navgrp)
17044 this.groups[navgrp.navId] = navgrp;
17048 * fetch a Navigation Group based on the navigation ID
17049 * if one does not exist , it will get created.
17050 * @param {string} the navgroup to add
17051 * @returns {Roo.bootstrap.NavGroup} the navgroup
17053 get: function(navId) {
17054 if (typeof(this.groups[navId]) == 'undefined') {
17055 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17057 return this.groups[navId] ;
17072 * @class Roo.bootstrap.TabPanel
17073 * @extends Roo.bootstrap.Component
17074 * Bootstrap TabPanel class
17075 * @cfg {Boolean} active panel active
17076 * @cfg {String} html panel content
17077 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17078 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17079 * @cfg {String} href click to link..
17083 * Create a new TabPanel
17084 * @param {Object} config The config object
17087 Roo.bootstrap.TabPanel = function(config){
17088 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17092 * Fires when the active status changes
17093 * @param {Roo.bootstrap.TabPanel} this
17094 * @param {Boolean} state the new state
17099 * @event beforedeactivate
17100 * Fires before a tab is de-activated - can be used to do validation on a form.
17101 * @param {Roo.bootstrap.TabPanel} this
17102 * @return {Boolean} false if there is an error
17105 'beforedeactivate': true
17108 this.tabId = this.tabId || Roo.id();
17112 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17120 getAutoCreate : function(){
17123 // item is needed for carousel - not sure if it has any effect otherwise
17124 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17125 html: this.html || ''
17129 cfg.cls += ' active';
17133 cfg.tabId = this.tabId;
17140 initEvents: function()
17142 var p = this.parent();
17143 this.navId = this.navId || p.navId;
17145 if (typeof(this.navId) != 'undefined') {
17146 // not really needed.. but just in case.. parent should be a NavGroup.
17147 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17151 var i = tg.tabs.length - 1;
17153 if(this.active && tg.bullets > 0 && i < tg.bullets){
17154 tg.setActiveBullet(i);
17158 if(this.href.length){
17159 this.el.on('click', this.onClick, this);
17164 onRender : function(ct, position)
17166 // Roo.log("Call onRender: " + this.xtype);
17168 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17176 setActive: function(state)
17178 Roo.log("panel - set active " + this.tabId + "=" + state);
17180 this.active = state;
17182 this.el.removeClass('active');
17184 } else if (!this.el.hasClass('active')) {
17185 this.el.addClass('active');
17188 this.fireEvent('changed', this, state);
17191 onClick: function(e)
17193 e.preventDefault();
17195 window.location.href = this.href;
17212 * @class Roo.bootstrap.DateField
17213 * @extends Roo.bootstrap.Input
17214 * Bootstrap DateField class
17215 * @cfg {Number} weekStart default 0
17216 * @cfg {String} viewMode default empty, (months|years)
17217 * @cfg {String} minViewMode default empty, (months|years)
17218 * @cfg {Number} startDate default -Infinity
17219 * @cfg {Number} endDate default Infinity
17220 * @cfg {Boolean} todayHighlight default false
17221 * @cfg {Boolean} todayBtn default false
17222 * @cfg {Boolean} calendarWeeks default false
17223 * @cfg {Object} daysOfWeekDisabled default empty
17224 * @cfg {Boolean} singleMode default false (true | false)
17226 * @cfg {Boolean} keyboardNavigation default true
17227 * @cfg {String} language default en
17230 * Create a new DateField
17231 * @param {Object} config The config object
17234 Roo.bootstrap.DateField = function(config){
17235 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17239 * Fires when this field show.
17240 * @param {Roo.bootstrap.DateField} this
17241 * @param {Mixed} date The date value
17246 * Fires when this field hide.
17247 * @param {Roo.bootstrap.DateField} this
17248 * @param {Mixed} date The date value
17253 * Fires when select a date.
17254 * @param {Roo.bootstrap.DateField} this
17255 * @param {Mixed} date The date value
17259 * @event beforeselect
17260 * Fires when before select a date.
17261 * @param {Roo.bootstrap.DateField} this
17262 * @param {Mixed} date The date value
17264 beforeselect : true
17268 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
17271 * @cfg {String} format
17272 * The default date format string which can be overriden for localization support. The format must be
17273 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17277 * @cfg {String} altFormats
17278 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17279 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17281 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17289 todayHighlight : false,
17295 keyboardNavigation: true,
17297 calendarWeeks: false,
17299 startDate: -Infinity,
17303 daysOfWeekDisabled: [],
17307 singleMode : false,
17309 UTCDate: function()
17311 return new Date(Date.UTC.apply(Date, arguments));
17314 UTCToday: function()
17316 var today = new Date();
17317 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17320 getDate: function() {
17321 var d = this.getUTCDate();
17322 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17325 getUTCDate: function() {
17329 setDate: function(d) {
17330 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17333 setUTCDate: function(d) {
17335 this.setValue(this.formatDate(this.date));
17338 onRender: function(ct, position)
17341 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17343 this.language = this.language || 'en';
17344 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17345 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17347 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17348 this.format = this.format || 'm/d/y';
17349 this.isInline = false;
17350 this.isInput = true;
17351 this.component = this.el.select('.add-on', true).first() || false;
17352 this.component = (this.component && this.component.length === 0) ? false : this.component;
17353 this.hasInput = this.component && this.inputEl().length;
17355 if (typeof(this.minViewMode === 'string')) {
17356 switch (this.minViewMode) {
17358 this.minViewMode = 1;
17361 this.minViewMode = 2;
17364 this.minViewMode = 0;
17369 if (typeof(this.viewMode === 'string')) {
17370 switch (this.viewMode) {
17383 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17385 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17387 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17389 this.picker().on('mousedown', this.onMousedown, this);
17390 this.picker().on('click', this.onClick, this);
17392 this.picker().addClass('datepicker-dropdown');
17394 this.startViewMode = this.viewMode;
17396 if(this.singleMode){
17397 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17398 v.setVisibilityMode(Roo.Element.DISPLAY);
17402 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17403 v.setStyle('width', '189px');
17407 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17408 if(!this.calendarWeeks){
17413 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17414 v.attr('colspan', function(i, val){
17415 return parseInt(val) + 1;
17420 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17422 this.setStartDate(this.startDate);
17423 this.setEndDate(this.endDate);
17425 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17432 if(this.isInline) {
17437 picker : function()
17439 return this.pickerEl;
17440 // return this.el.select('.datepicker', true).first();
17443 fillDow: function()
17445 var dowCnt = this.weekStart;
17454 if(this.calendarWeeks){
17462 while (dowCnt < this.weekStart + 7) {
17466 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17470 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17473 fillMonths: function()
17476 var months = this.picker().select('>.datepicker-months td', true).first();
17478 months.dom.innerHTML = '';
17484 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17487 months.createChild(month);
17494 this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
17496 if (this.date < this.startDate) {
17497 this.viewDate = new Date(this.startDate);
17498 } else if (this.date > this.endDate) {
17499 this.viewDate = new Date(this.endDate);
17501 this.viewDate = new Date(this.date);
17509 var d = new Date(this.viewDate),
17510 year = d.getUTCFullYear(),
17511 month = d.getUTCMonth(),
17512 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17513 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17514 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17515 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17516 currentDate = this.date && this.date.valueOf(),
17517 today = this.UTCToday();
17519 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17521 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17523 // this.picker.select('>tfoot th.today').
17524 // .text(dates[this.language].today)
17525 // .toggle(this.todayBtn !== false);
17527 this.updateNavArrows();
17530 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17532 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17534 prevMonth.setUTCDate(day);
17536 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17538 var nextMonth = new Date(prevMonth);
17540 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17542 nextMonth = nextMonth.valueOf();
17544 var fillMonths = false;
17546 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17548 while(prevMonth.valueOf() < nextMonth) {
17551 if (prevMonth.getUTCDay() === this.weekStart) {
17553 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17561 if(this.calendarWeeks){
17562 // ISO 8601: First week contains first thursday.
17563 // ISO also states week starts on Monday, but we can be more abstract here.
17565 // Start of current week: based on weekstart/current date
17566 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17567 // Thursday of this week
17568 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17569 // First Thursday of year, year from thursday
17570 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17571 // Calendar week: ms between thursdays, div ms per day, div 7 days
17572 calWeek = (th - yth) / 864e5 / 7 + 1;
17574 fillMonths.cn.push({
17582 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17584 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17587 if (this.todayHighlight &&
17588 prevMonth.getUTCFullYear() == today.getFullYear() &&
17589 prevMonth.getUTCMonth() == today.getMonth() &&
17590 prevMonth.getUTCDate() == today.getDate()) {
17591 clsName += ' today';
17594 if (currentDate && prevMonth.valueOf() === currentDate) {
17595 clsName += ' active';
17598 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17599 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17600 clsName += ' disabled';
17603 fillMonths.cn.push({
17605 cls: 'day ' + clsName,
17606 html: prevMonth.getDate()
17609 prevMonth.setDate(prevMonth.getDate()+1);
17612 var currentYear = this.date && this.date.getUTCFullYear();
17613 var currentMonth = this.date && this.date.getUTCMonth();
17615 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17617 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17618 v.removeClass('active');
17620 if(currentYear === year && k === currentMonth){
17621 v.addClass('active');
17624 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17625 v.addClass('disabled');
17631 year = parseInt(year/10, 10) * 10;
17633 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17635 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17638 for (var i = -1; i < 11; i++) {
17639 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17641 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17649 showMode: function(dir)
17652 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17655 Roo.each(this.picker().select('>div',true).elements, function(v){
17656 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17659 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17664 if(this.isInline) {
17668 this.picker().removeClass(['bottom', 'top']);
17670 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17672 * place to the top of element!
17676 this.picker().addClass('top');
17677 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17682 this.picker().addClass('bottom');
17684 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17687 parseDate : function(value)
17689 if(!value || value instanceof Date){
17692 var v = Date.parseDate(value, this.format);
17693 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17694 v = Date.parseDate(value, 'Y-m-d');
17696 if(!v && this.altFormats){
17697 if(!this.altFormatsArray){
17698 this.altFormatsArray = this.altFormats.split("|");
17700 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17701 v = Date.parseDate(value, this.altFormatsArray[i]);
17707 formatDate : function(date, fmt)
17709 return (!date || !(date instanceof Date)) ?
17710 date : date.dateFormat(fmt || this.format);
17713 onFocus : function()
17715 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17719 onBlur : function()
17721 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17723 var d = this.inputEl().getValue();
17732 this.picker().show();
17736 this.fireEvent('show', this, this.date);
17741 if(this.isInline) {
17744 this.picker().hide();
17745 this.viewMode = this.startViewMode;
17748 this.fireEvent('hide', this, this.date);
17752 onMousedown: function(e)
17754 e.stopPropagation();
17755 e.preventDefault();
17760 Roo.bootstrap.DateField.superclass.keyup.call(this);
17764 setValue: function(v)
17766 if(this.fireEvent('beforeselect', this, v) !== false){
17767 var d = new Date(this.parseDate(v) ).clearTime();
17769 if(isNaN(d.getTime())){
17770 this.date = this.viewDate = '';
17771 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17775 v = this.formatDate(d);
17777 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17779 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17783 this.fireEvent('select', this, this.date);
17787 getValue: function()
17789 return this.formatDate(this.date);
17792 fireKey: function(e)
17794 if (!this.picker().isVisible()){
17795 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17801 var dateChanged = false,
17803 newDate, newViewDate;
17808 e.preventDefault();
17812 if (!this.keyboardNavigation) {
17815 dir = e.keyCode == 37 ? -1 : 1;
17818 newDate = this.moveYear(this.date, dir);
17819 newViewDate = this.moveYear(this.viewDate, dir);
17820 } else if (e.shiftKey){
17821 newDate = this.moveMonth(this.date, dir);
17822 newViewDate = this.moveMonth(this.viewDate, dir);
17824 newDate = new Date(this.date);
17825 newDate.setUTCDate(this.date.getUTCDate() + dir);
17826 newViewDate = new Date(this.viewDate);
17827 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17829 if (this.dateWithinRange(newDate)){
17830 this.date = newDate;
17831 this.viewDate = newViewDate;
17832 this.setValue(this.formatDate(this.date));
17834 e.preventDefault();
17835 dateChanged = true;
17840 if (!this.keyboardNavigation) {
17843 dir = e.keyCode == 38 ? -1 : 1;
17845 newDate = this.moveYear(this.date, dir);
17846 newViewDate = this.moveYear(this.viewDate, dir);
17847 } else if (e.shiftKey){
17848 newDate = this.moveMonth(this.date, dir);
17849 newViewDate = this.moveMonth(this.viewDate, dir);
17851 newDate = new Date(this.date);
17852 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17853 newViewDate = new Date(this.viewDate);
17854 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17856 if (this.dateWithinRange(newDate)){
17857 this.date = newDate;
17858 this.viewDate = newViewDate;
17859 this.setValue(this.formatDate(this.date));
17861 e.preventDefault();
17862 dateChanged = true;
17866 this.setValue(this.formatDate(this.date));
17868 e.preventDefault();
17871 this.setValue(this.formatDate(this.date));
17885 onClick: function(e)
17887 e.stopPropagation();
17888 e.preventDefault();
17890 var target = e.getTarget();
17892 if(target.nodeName.toLowerCase() === 'i'){
17893 target = Roo.get(target).dom.parentNode;
17896 var nodeName = target.nodeName;
17897 var className = target.className;
17898 var html = target.innerHTML;
17899 //Roo.log(nodeName);
17901 switch(nodeName.toLowerCase()) {
17903 switch(className) {
17909 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17910 switch(this.viewMode){
17912 this.viewDate = this.moveMonth(this.viewDate, dir);
17916 this.viewDate = this.moveYear(this.viewDate, dir);
17922 var date = new Date();
17923 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17925 this.setValue(this.formatDate(this.date));
17932 if (className.indexOf('disabled') < 0) {
17933 this.viewDate.setUTCDate(1);
17934 if (className.indexOf('month') > -1) {
17935 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17937 var year = parseInt(html, 10) || 0;
17938 this.viewDate.setUTCFullYear(year);
17942 if(this.singleMode){
17943 this.setValue(this.formatDate(this.viewDate));
17954 //Roo.log(className);
17955 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17956 var day = parseInt(html, 10) || 1;
17957 var year = this.viewDate.getUTCFullYear(),
17958 month = this.viewDate.getUTCMonth();
17960 if (className.indexOf('old') > -1) {
17967 } else if (className.indexOf('new') > -1) {
17975 //Roo.log([year,month,day]);
17976 this.date = this.UTCDate(year, month, day,0,0,0,0);
17977 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17979 //Roo.log(this.formatDate(this.date));
17980 this.setValue(this.formatDate(this.date));
17987 setStartDate: function(startDate)
17989 this.startDate = startDate || -Infinity;
17990 if (this.startDate !== -Infinity) {
17991 this.startDate = this.parseDate(this.startDate);
17994 this.updateNavArrows();
17997 setEndDate: function(endDate)
17999 this.endDate = endDate || Infinity;
18000 if (this.endDate !== Infinity) {
18001 this.endDate = this.parseDate(this.endDate);
18004 this.updateNavArrows();
18007 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18009 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18010 if (typeof(this.daysOfWeekDisabled) !== 'object') {
18011 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18013 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18014 return parseInt(d, 10);
18017 this.updateNavArrows();
18020 updateNavArrows: function()
18022 if(this.singleMode){
18026 var d = new Date(this.viewDate),
18027 year = d.getUTCFullYear(),
18028 month = d.getUTCMonth();
18030 Roo.each(this.picker().select('.prev', true).elements, function(v){
18032 switch (this.viewMode) {
18035 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18041 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18048 Roo.each(this.picker().select('.next', true).elements, function(v){
18050 switch (this.viewMode) {
18053 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18059 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18067 moveMonth: function(date, dir)
18072 var new_date = new Date(date.valueOf()),
18073 day = new_date.getUTCDate(),
18074 month = new_date.getUTCMonth(),
18075 mag = Math.abs(dir),
18077 dir = dir > 0 ? 1 : -1;
18080 // If going back one month, make sure month is not current month
18081 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18083 return new_date.getUTCMonth() == month;
18085 // If going forward one month, make sure month is as expected
18086 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18088 return new_date.getUTCMonth() != new_month;
18090 new_month = month + dir;
18091 new_date.setUTCMonth(new_month);
18092 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18093 if (new_month < 0 || new_month > 11) {
18094 new_month = (new_month + 12) % 12;
18097 // For magnitudes >1, move one month at a time...
18098 for (var i=0; i<mag; i++) {
18099 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18100 new_date = this.moveMonth(new_date, dir);
18102 // ...then reset the day, keeping it in the new month
18103 new_month = new_date.getUTCMonth();
18104 new_date.setUTCDate(day);
18106 return new_month != new_date.getUTCMonth();
18109 // Common date-resetting loop -- if date is beyond end of month, make it
18112 new_date.setUTCDate(--day);
18113 new_date.setUTCMonth(new_month);
18118 moveYear: function(date, dir)
18120 return this.moveMonth(date, dir*12);
18123 dateWithinRange: function(date)
18125 return date >= this.startDate && date <= this.endDate;
18131 this.picker().remove();
18134 validateValue : function(value)
18136 if(value.length < 1) {
18137 if(this.allowBlank){
18143 if(value.length < this.minLength){
18146 if(value.length > this.maxLength){
18150 var vt = Roo.form.VTypes;
18151 if(!vt[this.vtype](value, this)){
18155 if(typeof this.validator == "function"){
18156 var msg = this.validator(value);
18162 if(this.regex && !this.regex.test(value)){
18166 if(typeof(this.parseDate(value)) == 'undefined'){
18170 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18174 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18184 Roo.apply(Roo.bootstrap.DateField, {
18195 html: '<i class="fa fa-arrow-left"/>'
18205 html: '<i class="fa fa-arrow-right"/>'
18247 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18248 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18249 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18250 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18251 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18264 navFnc: 'FullYear',
18269 navFnc: 'FullYear',
18274 Roo.apply(Roo.bootstrap.DateField, {
18278 cls: 'datepicker dropdown-menu roo-dynamic',
18282 cls: 'datepicker-days',
18286 cls: 'table-condensed',
18288 Roo.bootstrap.DateField.head,
18292 Roo.bootstrap.DateField.footer
18299 cls: 'datepicker-months',
18303 cls: 'table-condensed',
18305 Roo.bootstrap.DateField.head,
18306 Roo.bootstrap.DateField.content,
18307 Roo.bootstrap.DateField.footer
18314 cls: 'datepicker-years',
18318 cls: 'table-condensed',
18320 Roo.bootstrap.DateField.head,
18321 Roo.bootstrap.DateField.content,
18322 Roo.bootstrap.DateField.footer
18341 * @class Roo.bootstrap.TimeField
18342 * @extends Roo.bootstrap.Input
18343 * Bootstrap DateField class
18347 * Create a new TimeField
18348 * @param {Object} config The config object
18351 Roo.bootstrap.TimeField = function(config){
18352 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18356 * Fires when this field show.
18357 * @param {Roo.bootstrap.DateField} thisthis
18358 * @param {Mixed} date The date value
18363 * Fires when this field hide.
18364 * @param {Roo.bootstrap.DateField} this
18365 * @param {Mixed} date The date value
18370 * Fires when select a date.
18371 * @param {Roo.bootstrap.DateField} this
18372 * @param {Mixed} date The date value
18378 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18381 * @cfg {String} format
18382 * The default time format string which can be overriden for localization support. The format must be
18383 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18387 onRender: function(ct, position)
18390 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18392 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18394 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18396 this.pop = this.picker().select('>.datepicker-time',true).first();
18397 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18399 this.picker().on('mousedown', this.onMousedown, this);
18400 this.picker().on('click', this.onClick, this);
18402 this.picker().addClass('datepicker-dropdown');
18407 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18408 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18409 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18410 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18411 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18412 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18416 fireKey: function(e){
18417 if (!this.picker().isVisible()){
18418 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18424 e.preventDefault();
18432 this.onTogglePeriod();
18435 this.onIncrementMinutes();
18438 this.onDecrementMinutes();
18447 onClick: function(e) {
18448 e.stopPropagation();
18449 e.preventDefault();
18452 picker : function()
18454 return this.el.select('.datepicker', true).first();
18457 fillTime: function()
18459 var time = this.pop.select('tbody', true).first();
18461 time.dom.innerHTML = '';
18476 cls: 'hours-up glyphicon glyphicon-chevron-up'
18496 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18517 cls: 'timepicker-hour',
18532 cls: 'timepicker-minute',
18547 cls: 'btn btn-primary period',
18569 cls: 'hours-down glyphicon glyphicon-chevron-down'
18589 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18607 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18614 var hours = this.time.getHours();
18615 var minutes = this.time.getMinutes();
18628 hours = hours - 12;
18632 hours = '0' + hours;
18636 minutes = '0' + minutes;
18639 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18640 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18641 this.pop.select('button', true).first().dom.innerHTML = period;
18647 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18649 var cls = ['bottom'];
18651 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18658 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18663 this.picker().addClass(cls.join('-'));
18667 Roo.each(cls, function(c){
18669 _this.picker().setTop(_this.inputEl().getHeight());
18673 _this.picker().setTop(0 - _this.picker().getHeight());
18678 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18682 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18689 onFocus : function()
18691 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18695 onBlur : function()
18697 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18703 this.picker().show();
18708 this.fireEvent('show', this, this.date);
18713 this.picker().hide();
18716 this.fireEvent('hide', this, this.date);
18719 setTime : function()
18722 this.setValue(this.time.format(this.format));
18724 this.fireEvent('select', this, this.date);
18729 onMousedown: function(e){
18730 e.stopPropagation();
18731 e.preventDefault();
18734 onIncrementHours: function()
18736 Roo.log('onIncrementHours');
18737 this.time = this.time.add(Date.HOUR, 1);
18742 onDecrementHours: function()
18744 Roo.log('onDecrementHours');
18745 this.time = this.time.add(Date.HOUR, -1);
18749 onIncrementMinutes: function()
18751 Roo.log('onIncrementMinutes');
18752 this.time = this.time.add(Date.MINUTE, 1);
18756 onDecrementMinutes: function()
18758 Roo.log('onDecrementMinutes');
18759 this.time = this.time.add(Date.MINUTE, -1);
18763 onTogglePeriod: function()
18765 Roo.log('onTogglePeriod');
18766 this.time = this.time.add(Date.HOUR, 12);
18773 Roo.apply(Roo.bootstrap.TimeField, {
18803 cls: 'btn btn-info ok',
18815 Roo.apply(Roo.bootstrap.TimeField, {
18819 cls: 'datepicker dropdown-menu',
18823 cls: 'datepicker-time',
18827 cls: 'table-condensed',
18829 Roo.bootstrap.TimeField.content,
18830 Roo.bootstrap.TimeField.footer
18849 * @class Roo.bootstrap.MonthField
18850 * @extends Roo.bootstrap.Input
18851 * Bootstrap MonthField class
18853 * @cfg {String} language default en
18856 * Create a new MonthField
18857 * @param {Object} config The config object
18860 Roo.bootstrap.MonthField = function(config){
18861 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18866 * Fires when this field show.
18867 * @param {Roo.bootstrap.MonthField} this
18868 * @param {Mixed} date The date value
18873 * Fires when this field hide.
18874 * @param {Roo.bootstrap.MonthField} this
18875 * @param {Mixed} date The date value
18880 * Fires when select a date.
18881 * @param {Roo.bootstrap.MonthField} this
18882 * @param {String} oldvalue The old value
18883 * @param {String} newvalue The new value
18889 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18891 onRender: function(ct, position)
18894 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18896 this.language = this.language || 'en';
18897 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18898 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18900 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18901 this.isInline = false;
18902 this.isInput = true;
18903 this.component = this.el.select('.add-on', true).first() || false;
18904 this.component = (this.component && this.component.length === 0) ? false : this.component;
18905 this.hasInput = this.component && this.inputEL().length;
18907 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18909 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18911 this.picker().on('mousedown', this.onMousedown, this);
18912 this.picker().on('click', this.onClick, this);
18914 this.picker().addClass('datepicker-dropdown');
18916 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18917 v.setStyle('width', '189px');
18924 if(this.isInline) {
18930 setValue: function(v, suppressEvent)
18932 var o = this.getValue();
18934 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18938 if(suppressEvent !== true){
18939 this.fireEvent('select', this, o, v);
18944 getValue: function()
18949 onClick: function(e)
18951 e.stopPropagation();
18952 e.preventDefault();
18954 var target = e.getTarget();
18956 if(target.nodeName.toLowerCase() === 'i'){
18957 target = Roo.get(target).dom.parentNode;
18960 var nodeName = target.nodeName;
18961 var className = target.className;
18962 var html = target.innerHTML;
18964 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18968 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18970 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18976 picker : function()
18978 return this.pickerEl;
18981 fillMonths: function()
18984 var months = this.picker().select('>.datepicker-months td', true).first();
18986 months.dom.innerHTML = '';
18992 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18995 months.createChild(month);
19004 if(typeof(this.vIndex) == 'undefined' && this.value.length){
19005 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19008 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19009 e.removeClass('active');
19011 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19012 e.addClass('active');
19019 if(this.isInline) {
19023 this.picker().removeClass(['bottom', 'top']);
19025 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19027 * place to the top of element!
19031 this.picker().addClass('top');
19032 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19037 this.picker().addClass('bottom');
19039 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19042 onFocus : function()
19044 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19048 onBlur : function()
19050 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19052 var d = this.inputEl().getValue();
19061 this.picker().show();
19062 this.picker().select('>.datepicker-months', true).first().show();
19066 this.fireEvent('show', this, this.date);
19071 if(this.isInline) {
19074 this.picker().hide();
19075 this.fireEvent('hide', this, this.date);
19079 onMousedown: function(e)
19081 e.stopPropagation();
19082 e.preventDefault();
19087 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19091 fireKey: function(e)
19093 if (!this.picker().isVisible()){
19094 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19105 e.preventDefault();
19109 dir = e.keyCode == 37 ? -1 : 1;
19111 this.vIndex = this.vIndex + dir;
19113 if(this.vIndex < 0){
19117 if(this.vIndex > 11){
19121 if(isNaN(this.vIndex)){
19125 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19131 dir = e.keyCode == 38 ? -1 : 1;
19133 this.vIndex = this.vIndex + dir * 4;
19135 if(this.vIndex < 0){
19139 if(this.vIndex > 11){
19143 if(isNaN(this.vIndex)){
19147 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19152 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19153 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19157 e.preventDefault();
19160 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19161 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19177 this.picker().remove();
19182 Roo.apply(Roo.bootstrap.MonthField, {
19201 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19202 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19207 Roo.apply(Roo.bootstrap.MonthField, {
19211 cls: 'datepicker dropdown-menu roo-dynamic',
19215 cls: 'datepicker-months',
19219 cls: 'table-condensed',
19221 Roo.bootstrap.DateField.content
19241 * @class Roo.bootstrap.CheckBox
19242 * @extends Roo.bootstrap.Input
19243 * Bootstrap CheckBox class
19245 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19246 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19247 * @cfg {String} boxLabel The text that appears beside the checkbox
19248 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19249 * @cfg {Boolean} checked initnal the element
19250 * @cfg {Boolean} inline inline the element (default false)
19251 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19254 * Create a new CheckBox
19255 * @param {Object} config The config object
19258 Roo.bootstrap.CheckBox = function(config){
19259 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19264 * Fires when the element is checked or unchecked.
19265 * @param {Roo.bootstrap.CheckBox} this This input
19266 * @param {Boolean} checked The new checked value
19273 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
19275 inputType: 'checkbox',
19283 getAutoCreate : function()
19285 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19291 cfg.cls = 'form-group ' + this.inputType; //input-group
19294 cfg.cls += ' ' + this.inputType + '-inline';
19300 type : this.inputType,
19301 value : this.inputValue,
19302 cls : 'roo-' + this.inputType, //'form-box',
19303 placeholder : this.placeholder || ''
19311 cls : 'roo-hidden-value',
19312 value : this.checked ? this.valueOff : this.inputValue
19315 if (this.weight) { // Validity check?
19316 cfg.cls += " " + this.inputType + "-" + this.weight;
19319 if (this.disabled) {
19320 input.disabled=true;
19324 input.checked = this.checked;
19330 hidden.name = this.name;
19331 input.name = '_hidden_' + this.name;
19335 input.cls += ' input-' + this.size;
19340 ['xs','sm','md','lg'].map(function(size){
19341 if (settings[size]) {
19342 cfg.cls += ' col-' + size + '-' + settings[size];
19354 if (this.before || this.after) {
19357 cls : 'input-group',
19362 inputblock.cn.push({
19364 cls : 'input-group-addon',
19369 inputblock.cn.push(input);
19370 inputblock.cn.push(hidden);
19373 inputblock.cn.push({
19375 cls : 'input-group-addon',
19382 if (align ==='left' && this.fieldLabel.length) {
19383 // Roo.log("left and has label");
19389 cls : 'control-label col-md-' + this.labelWidth,
19390 html : this.fieldLabel
19394 cls : "col-md-" + (12 - this.labelWidth),
19401 } else if ( this.fieldLabel.length) {
19402 // Roo.log(" label");
19406 tag: this.boxLabel ? 'span' : 'label',
19408 cls: 'control-label box-input-label',
19409 //cls : 'input-group-addon',
19410 html : this.fieldLabel
19420 // Roo.log(" no label && no align");
19421 cfg.cn = [ inputblock ] ;
19427 var boxLabelCfg = {
19429 //'for': id, // box label is handled by onclick - so no for...
19431 html: this.boxLabel
19435 boxLabelCfg.tooltip = this.tooltip;
19438 cfg.cn.push(boxLabelCfg);
19448 * return the real input element.
19450 inputEl: function ()
19452 return this.el.select('input.roo-' + this.inputType,true).first();
19454 hiddenEl: function ()
19456 return this.el.select('input.hidden-value',true).first();
19459 labelEl: function()
19461 return this.el.select('label.control-label',true).first();
19463 /* depricated... */
19467 return this.labelEl();
19470 boxLabelEl: function()
19472 return this.el.select('label.box-label',true).first();
19475 initEvents : function()
19477 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19479 this.inputEl().on('click', this.onClick, this);
19481 if (this.boxLabel) {
19482 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19485 this.startValue = this.getValue();
19488 Roo.bootstrap.CheckBox.register(this);
19492 onClick : function()
19494 this.setChecked(!this.checked);
19497 setChecked : function(state,suppressEvent)
19499 this.startValue = this.getValue();
19501 if(this.inputType == 'radio'){
19503 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19504 e.dom.checked = false;
19507 this.inputEl().dom.checked = true;
19509 this.inputEl().dom.value = this.inputValue;
19511 if(suppressEvent !== true){
19512 this.fireEvent('check', this, true);
19520 this.checked = state;
19522 this.inputEl().dom.checked = state;
19525 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19527 if(suppressEvent !== true){
19528 this.fireEvent('check', this, state);
19534 getValue : function()
19536 if(this.inputType == 'radio'){
19537 return this.getGroupValue();
19540 return this.hiddenEl() ? this.hiddenEl().dom.value : this.value;
19544 getGroupValue : function()
19546 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19550 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19553 setValue : function(v,suppressEvent)
19555 if(this.inputType == 'radio'){
19556 this.setGroupValue(v, suppressEvent);
19560 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19565 setGroupValue : function(v, suppressEvent)
19567 this.startValue = this.getValue();
19569 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19570 e.dom.checked = false;
19572 if(e.dom.value == v){
19573 e.dom.checked = true;
19577 if(suppressEvent !== true){
19578 this.fireEvent('check', this, true);
19586 validate : function()
19590 (this.inputType == 'radio' && this.validateRadio()) ||
19591 (this.inputType == 'checkbox' && this.validateCheckbox())
19597 this.markInvalid();
19601 validateRadio : function()
19603 if(this.allowBlank){
19609 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19610 if(!e.dom.checked){
19622 validateCheckbox : function()
19625 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19628 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19636 for(var i in group){
19641 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19648 * Mark this field as valid
19650 markValid : function()
19652 if(this.allowBlank){
19658 this.fireEvent('valid', this);
19660 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19663 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19670 if(this.inputType == 'radio'){
19671 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19672 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19673 e.findParent('.form-group', false, true).addClass(_this.validClass);
19680 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19681 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19685 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19691 for(var i in group){
19692 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19693 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19698 * Mark this field as invalid
19699 * @param {String} msg The validation message
19701 markInvalid : function(msg)
19703 if(this.allowBlank){
19709 this.fireEvent('invalid', this, msg);
19711 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19714 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19718 label.markInvalid();
19721 if(this.inputType == 'radio'){
19722 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19723 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19724 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19731 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19732 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19736 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19742 for(var i in group){
19743 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19744 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19749 disable : function()
19751 if(this.inputType != 'radio'){
19752 Roo.bootstrap.CheckBox.superclass.disable.call(this);
19759 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19760 _this.getActionEl().addClass(this.disabledClass);
19761 e.dom.disabled = true;
19765 this.disabled = true;
19766 this.fireEvent("disable", this);
19770 enable : function()
19772 if(this.inputType != 'radio'){
19773 Roo.bootstrap.CheckBox.superclass.enable.call(this);
19780 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19781 _this.getActionEl().removeClass(this.disabledClass);
19782 e.dom.disabled = false;
19786 this.disabled = false;
19787 this.fireEvent("enable", this);
19794 Roo.apply(Roo.bootstrap.CheckBox, {
19799 * register a CheckBox Group
19800 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19802 register : function(checkbox)
19804 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19805 this.groups[checkbox.groupId] = {};
19808 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19812 this.groups[checkbox.groupId][checkbox.name] = checkbox;
19816 * fetch a CheckBox Group based on the group ID
19817 * @param {string} the group ID
19818 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19820 get: function(groupId) {
19821 if (typeof(this.groups[groupId]) == 'undefined') {
19825 return this.groups[groupId] ;
19837 *<div class="radio">
19839 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19840 Option one is this and that—be sure to include why it's great
19847 *<label class="radio-inline">fieldLabel</label>
19848 *<label class="radio-inline">
19849 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19857 * @class Roo.bootstrap.Radio
19858 * @extends Roo.bootstrap.CheckBox
19859 * Bootstrap Radio class
19862 * Create a new Radio
19863 * @param {Object} config The config object
19866 Roo.bootstrap.Radio = function(config){
19867 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19871 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19873 inputType: 'radio',
19877 getAutoCreate : function()
19879 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19880 align = align || 'left'; // default...
19887 tag : this.inline ? 'span' : 'div',
19892 var inline = this.inline ? ' radio-inline' : '';
19896 // does not need for, as we wrap the input with it..
19898 cls : 'control-label box-label' + inline,
19901 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19905 //cls : 'control-label' + inline,
19906 html : this.fieldLabel,
19907 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19916 type : this.inputType,
19917 //value : (!this.checked) ? this.valueOff : this.inputValue,
19918 value : this.inputValue,
19920 placeholder : this.placeholder || '' // ?? needed????
19923 if (this.weight) { // Validity check?
19924 input.cls += " radio-" + this.weight;
19926 if (this.disabled) {
19927 input.disabled=true;
19931 input.checked = this.checked;
19935 input.name = this.name;
19939 input.cls += ' input-' + this.size;
19942 //?? can span's inline have a width??
19945 ['xs','sm','md','lg'].map(function(size){
19946 if (settings[size]) {
19947 cfg.cls += ' col-' + size + '-' + settings[size];
19951 var inputblock = input;
19953 if (this.before || this.after) {
19956 cls : 'input-group',
19961 inputblock.cn.push({
19963 cls : 'input-group-addon',
19967 inputblock.cn.push(input);
19969 inputblock.cn.push({
19971 cls : 'input-group-addon',
19979 if (this.fieldLabel && this.fieldLabel.length) {
19980 cfg.cn.push(fieldLabel);
19983 // normal bootstrap puts the input inside the label.
19984 // however with our styled version - it has to go after the input.
19986 //lbl.cn.push(inputblock);
19990 cls: 'radio' + inline,
19997 cfg.cn.push( lblwrap);
20002 html: this.boxLabel
20011 initEvents : function()
20013 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20015 this.inputEl().on('click', this.onClick, this);
20016 if (this.boxLabel) {
20017 //Roo.log('find label');
20018 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
20023 inputEl: function ()
20025 return this.el.select('input.roo-radio',true).first();
20027 onClick : function()
20030 this.setChecked(true);
20033 setChecked : function(state,suppressEvent)
20036 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20037 v.dom.checked = false;
20040 Roo.log(this.inputEl().dom);
20041 this.checked = state;
20042 this.inputEl().dom.checked = state;
20044 if(suppressEvent !== true){
20045 this.fireEvent('check', this, state);
20048 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20052 getGroupValue : function()
20055 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20056 if(v.dom.checked == true){
20057 value = v.dom.value;
20065 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
20066 * @return {Mixed} value The field value
20068 getValue : function(){
20069 return this.getGroupValue();
20075 //<script type="text/javascript">
20078 * Based Ext JS Library 1.1.1
20079 * Copyright(c) 2006-2007, Ext JS, LLC.
20085 * @class Roo.HtmlEditorCore
20086 * @extends Roo.Component
20087 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20089 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20092 Roo.HtmlEditorCore = function(config){
20095 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20100 * @event initialize
20101 * Fires when the editor is fully initialized (including the iframe)
20102 * @param {Roo.HtmlEditorCore} this
20107 * Fires when the editor is first receives the focus. Any insertion must wait
20108 * until after this event.
20109 * @param {Roo.HtmlEditorCore} this
20113 * @event beforesync
20114 * Fires before the textarea is updated with content from the editor iframe. Return false
20115 * to cancel the sync.
20116 * @param {Roo.HtmlEditorCore} this
20117 * @param {String} html
20121 * @event beforepush
20122 * Fires before the iframe editor is updated with content from the textarea. Return false
20123 * to cancel the push.
20124 * @param {Roo.HtmlEditorCore} this
20125 * @param {String} html
20130 * Fires when the textarea is updated with content from the editor iframe.
20131 * @param {Roo.HtmlEditorCore} this
20132 * @param {String} html
20137 * Fires when the iframe editor is updated with content from the textarea.
20138 * @param {Roo.HtmlEditorCore} this
20139 * @param {String} html
20144 * @event editorevent
20145 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20146 * @param {Roo.HtmlEditorCore} this
20152 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20154 // defaults : white / black...
20155 this.applyBlacklists();
20162 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20166 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20172 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20177 * @cfg {Number} height (in pixels)
20181 * @cfg {Number} width (in pixels)
20186 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20189 stylesheets: false,
20194 // private properties
20195 validationEvent : false,
20197 initialized : false,
20199 sourceEditMode : false,
20200 onFocus : Roo.emptyFn,
20202 hideMode:'offsets',
20206 // blacklist + whitelisted elements..
20213 * Protected method that will not generally be called directly. It
20214 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20215 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20217 getDocMarkup : function(){
20221 // inherit styels from page...??
20222 if (this.stylesheets === false) {
20224 Roo.get(document.head).select('style').each(function(node) {
20225 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20228 Roo.get(document.head).select('link').each(function(node) {
20229 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20232 } else if (!this.stylesheets.length) {
20234 st = '<style type="text/css">' +
20235 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20241 st += '<style type="text/css">' +
20242 'IMG { cursor: pointer } ' +
20246 return '<html><head>' + st +
20247 //<style type="text/css">' +
20248 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20250 ' </head><body class="roo-htmleditor-body"></body></html>';
20254 onRender : function(ct, position)
20257 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20258 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20261 this.el.dom.style.border = '0 none';
20262 this.el.dom.setAttribute('tabIndex', -1);
20263 this.el.addClass('x-hidden hide');
20267 if(Roo.isIE){ // fix IE 1px bogus margin
20268 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20272 this.frameId = Roo.id();
20276 var iframe = this.owner.wrap.createChild({
20278 cls: 'form-control', // bootstrap..
20280 name: this.frameId,
20281 frameBorder : 'no',
20282 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20287 this.iframe = iframe.dom;
20289 this.assignDocWin();
20291 this.doc.designMode = 'on';
20294 this.doc.write(this.getDocMarkup());
20298 var task = { // must defer to wait for browser to be ready
20300 //console.log("run task?" + this.doc.readyState);
20301 this.assignDocWin();
20302 if(this.doc.body || this.doc.readyState == 'complete'){
20304 this.doc.designMode="on";
20308 Roo.TaskMgr.stop(task);
20309 this.initEditor.defer(10, this);
20316 Roo.TaskMgr.start(task);
20321 onResize : function(w, h)
20323 Roo.log('resize: ' +w + ',' + h );
20324 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20328 if(typeof w == 'number'){
20330 this.iframe.style.width = w + 'px';
20332 if(typeof h == 'number'){
20334 this.iframe.style.height = h + 'px';
20336 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20343 * Toggles the editor between standard and source edit mode.
20344 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20346 toggleSourceEdit : function(sourceEditMode){
20348 this.sourceEditMode = sourceEditMode === true;
20350 if(this.sourceEditMode){
20352 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20355 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20356 //this.iframe.className = '';
20359 //this.setSize(this.owner.wrap.getSize());
20360 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20367 * Protected method that will not generally be called directly. If you need/want
20368 * custom HTML cleanup, this is the method you should override.
20369 * @param {String} html The HTML to be cleaned
20370 * return {String} The cleaned HTML
20372 cleanHtml : function(html){
20373 html = String(html);
20374 if(html.length > 5){
20375 if(Roo.isSafari){ // strip safari nonsense
20376 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20379 if(html == ' '){
20386 * HTML Editor -> Textarea
20387 * Protected method that will not generally be called directly. Syncs the contents
20388 * of the editor iframe with the textarea.
20390 syncValue : function(){
20391 if(this.initialized){
20392 var bd = (this.doc.body || this.doc.documentElement);
20393 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20394 var html = bd.innerHTML;
20396 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20397 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20399 html = '<div style="'+m[0]+'">' + html + '</div>';
20402 html = this.cleanHtml(html);
20403 // fix up the special chars.. normaly like back quotes in word...
20404 // however we do not want to do this with chinese..
20405 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20406 var cc = b.charCodeAt();
20408 (cc >= 0x4E00 && cc < 0xA000 ) ||
20409 (cc >= 0x3400 && cc < 0x4E00 ) ||
20410 (cc >= 0xf900 && cc < 0xfb00 )
20416 if(this.owner.fireEvent('beforesync', this, html) !== false){
20417 this.el.dom.value = html;
20418 this.owner.fireEvent('sync', this, html);
20424 * Protected method that will not generally be called directly. Pushes the value of the textarea
20425 * into the iframe editor.
20427 pushValue : function(){
20428 if(this.initialized){
20429 var v = this.el.dom.value.trim();
20431 // if(v.length < 1){
20435 if(this.owner.fireEvent('beforepush', this, v) !== false){
20436 var d = (this.doc.body || this.doc.documentElement);
20438 this.cleanUpPaste();
20439 this.el.dom.value = d.innerHTML;
20440 this.owner.fireEvent('push', this, v);
20446 deferFocus : function(){
20447 this.focus.defer(10, this);
20451 focus : function(){
20452 if(this.win && !this.sourceEditMode){
20459 assignDocWin: function()
20461 var iframe = this.iframe;
20464 this.doc = iframe.contentWindow.document;
20465 this.win = iframe.contentWindow;
20467 // if (!Roo.get(this.frameId)) {
20470 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20471 // this.win = Roo.get(this.frameId).dom.contentWindow;
20473 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20477 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20478 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20483 initEditor : function(){
20484 //console.log("INIT EDITOR");
20485 this.assignDocWin();
20489 this.doc.designMode="on";
20491 this.doc.write(this.getDocMarkup());
20494 var dbody = (this.doc.body || this.doc.documentElement);
20495 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20496 // this copies styles from the containing element into thsi one..
20497 // not sure why we need all of this..
20498 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20500 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20501 //ss['background-attachment'] = 'fixed'; // w3c
20502 dbody.bgProperties = 'fixed'; // ie
20503 //Roo.DomHelper.applyStyles(dbody, ss);
20504 Roo.EventManager.on(this.doc, {
20505 //'mousedown': this.onEditorEvent,
20506 'mouseup': this.onEditorEvent,
20507 'dblclick': this.onEditorEvent,
20508 'click': this.onEditorEvent,
20509 'keyup': this.onEditorEvent,
20514 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20516 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20517 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20519 this.initialized = true;
20521 this.owner.fireEvent('initialize', this);
20526 onDestroy : function(){
20532 //for (var i =0; i < this.toolbars.length;i++) {
20533 // // fixme - ask toolbars for heights?
20534 // this.toolbars[i].onDestroy();
20537 //this.wrap.dom.innerHTML = '';
20538 //this.wrap.remove();
20543 onFirstFocus : function(){
20545 this.assignDocWin();
20548 this.activated = true;
20551 if(Roo.isGecko){ // prevent silly gecko errors
20553 var s = this.win.getSelection();
20554 if(!s.focusNode || s.focusNode.nodeType != 3){
20555 var r = s.getRangeAt(0);
20556 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20561 this.execCmd('useCSS', true);
20562 this.execCmd('styleWithCSS', false);
20565 this.owner.fireEvent('activate', this);
20569 adjustFont: function(btn){
20570 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20571 //if(Roo.isSafari){ // safari
20574 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20575 if(Roo.isSafari){ // safari
20576 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20577 v = (v < 10) ? 10 : v;
20578 v = (v > 48) ? 48 : v;
20579 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20584 v = Math.max(1, v+adjust);
20586 this.execCmd('FontSize', v );
20589 onEditorEvent : function(e)
20591 this.owner.fireEvent('editorevent', this, e);
20592 // this.updateToolbar();
20593 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20596 insertTag : function(tg)
20598 // could be a bit smarter... -> wrap the current selected tRoo..
20599 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20601 range = this.createRange(this.getSelection());
20602 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20603 wrappingNode.appendChild(range.extractContents());
20604 range.insertNode(wrappingNode);
20611 this.execCmd("formatblock", tg);
20615 insertText : function(txt)
20619 var range = this.createRange();
20620 range.deleteContents();
20621 //alert(Sender.getAttribute('label'));
20623 range.insertNode(this.doc.createTextNode(txt));
20629 * Executes a Midas editor command on the editor document and performs necessary focus and
20630 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20631 * @param {String} cmd The Midas command
20632 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20634 relayCmd : function(cmd, value){
20636 this.execCmd(cmd, value);
20637 this.owner.fireEvent('editorevent', this);
20638 //this.updateToolbar();
20639 this.owner.deferFocus();
20643 * Executes a Midas editor command directly on the editor document.
20644 * For visual commands, you should use {@link #relayCmd} instead.
20645 * <b>This should only be called after the editor is initialized.</b>
20646 * @param {String} cmd The Midas command
20647 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20649 execCmd : function(cmd, value){
20650 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20657 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20659 * @param {String} text | dom node..
20661 insertAtCursor : function(text)
20666 if(!this.activated){
20672 var r = this.doc.selection.createRange();
20683 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20687 // from jquery ui (MIT licenced)
20689 var win = this.win;
20691 if (win.getSelection && win.getSelection().getRangeAt) {
20692 range = win.getSelection().getRangeAt(0);
20693 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20694 range.insertNode(node);
20695 } else if (win.document.selection && win.document.selection.createRange) {
20696 // no firefox support
20697 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20698 win.document.selection.createRange().pasteHTML(txt);
20700 // no firefox support
20701 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20702 this.execCmd('InsertHTML', txt);
20711 mozKeyPress : function(e){
20713 var c = e.getCharCode(), cmd;
20716 c = String.fromCharCode(c).toLowerCase();
20730 this.cleanUpPaste.defer(100, this);
20738 e.preventDefault();
20746 fixKeys : function(){ // load time branching for fastest keydown performance
20748 return function(e){
20749 var k = e.getKey(), r;
20752 r = this.doc.selection.createRange();
20755 r.pasteHTML('    ');
20762 r = this.doc.selection.createRange();
20764 var target = r.parentElement();
20765 if(!target || target.tagName.toLowerCase() != 'li'){
20767 r.pasteHTML('<br />');
20773 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20774 this.cleanUpPaste.defer(100, this);
20780 }else if(Roo.isOpera){
20781 return function(e){
20782 var k = e.getKey();
20786 this.execCmd('InsertHTML','    ');
20789 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20790 this.cleanUpPaste.defer(100, this);
20795 }else if(Roo.isSafari){
20796 return function(e){
20797 var k = e.getKey();
20801 this.execCmd('InsertText','\t');
20805 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20806 this.cleanUpPaste.defer(100, this);
20814 getAllAncestors: function()
20816 var p = this.getSelectedNode();
20819 a.push(p); // push blank onto stack..
20820 p = this.getParentElement();
20824 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20828 a.push(this.doc.body);
20832 lastSelNode : false,
20835 getSelection : function()
20837 this.assignDocWin();
20838 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20841 getSelectedNode: function()
20843 // this may only work on Gecko!!!
20845 // should we cache this!!!!
20850 var range = this.createRange(this.getSelection()).cloneRange();
20853 var parent = range.parentElement();
20855 var testRange = range.duplicate();
20856 testRange.moveToElementText(parent);
20857 if (testRange.inRange(range)) {
20860 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20863 parent = parent.parentElement;
20868 // is ancestor a text element.
20869 var ac = range.commonAncestorContainer;
20870 if (ac.nodeType == 3) {
20871 ac = ac.parentNode;
20874 var ar = ac.childNodes;
20877 var other_nodes = [];
20878 var has_other_nodes = false;
20879 for (var i=0;i<ar.length;i++) {
20880 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20883 // fullly contained node.
20885 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20890 // probably selected..
20891 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20892 other_nodes.push(ar[i]);
20896 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20901 has_other_nodes = true;
20903 if (!nodes.length && other_nodes.length) {
20904 nodes= other_nodes;
20906 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20912 createRange: function(sel)
20914 // this has strange effects when using with
20915 // top toolbar - not sure if it's a great idea.
20916 //this.editor.contentWindow.focus();
20917 if (typeof sel != "undefined") {
20919 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20921 return this.doc.createRange();
20924 return this.doc.createRange();
20927 getParentElement: function()
20930 this.assignDocWin();
20931 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20933 var range = this.createRange(sel);
20936 var p = range.commonAncestorContainer;
20937 while (p.nodeType == 3) { // text node
20948 * Range intersection.. the hard stuff...
20952 * [ -- selected range --- ]
20956 * if end is before start or hits it. fail.
20957 * if start is after end or hits it fail.
20959 * if either hits (but other is outside. - then it's not
20965 // @see http://www.thismuchiknow.co.uk/?p=64.
20966 rangeIntersectsNode : function(range, node)
20968 var nodeRange = node.ownerDocument.createRange();
20970 nodeRange.selectNode(node);
20972 nodeRange.selectNodeContents(node);
20975 var rangeStartRange = range.cloneRange();
20976 rangeStartRange.collapse(true);
20978 var rangeEndRange = range.cloneRange();
20979 rangeEndRange.collapse(false);
20981 var nodeStartRange = nodeRange.cloneRange();
20982 nodeStartRange.collapse(true);
20984 var nodeEndRange = nodeRange.cloneRange();
20985 nodeEndRange.collapse(false);
20987 return rangeStartRange.compareBoundaryPoints(
20988 Range.START_TO_START, nodeEndRange) == -1 &&
20989 rangeEndRange.compareBoundaryPoints(
20990 Range.START_TO_START, nodeStartRange) == 1;
20994 rangeCompareNode : function(range, node)
20996 var nodeRange = node.ownerDocument.createRange();
20998 nodeRange.selectNode(node);
21000 nodeRange.selectNodeContents(node);
21004 range.collapse(true);
21006 nodeRange.collapse(true);
21008 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21009 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
21011 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21013 var nodeIsBefore = ss == 1;
21014 var nodeIsAfter = ee == -1;
21016 if (nodeIsBefore && nodeIsAfter) {
21019 if (!nodeIsBefore && nodeIsAfter) {
21020 return 1; //right trailed.
21023 if (nodeIsBefore && !nodeIsAfter) {
21024 return 2; // left trailed.
21030 // private? - in a new class?
21031 cleanUpPaste : function()
21033 // cleans up the whole document..
21034 Roo.log('cleanuppaste');
21036 this.cleanUpChildren(this.doc.body);
21037 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21038 if (clean != this.doc.body.innerHTML) {
21039 this.doc.body.innerHTML = clean;
21044 cleanWordChars : function(input) {// change the chars to hex code
21045 var he = Roo.HtmlEditorCore;
21047 var output = input;
21048 Roo.each(he.swapCodes, function(sw) {
21049 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21051 output = output.replace(swapper, sw[1]);
21058 cleanUpChildren : function (n)
21060 if (!n.childNodes.length) {
21063 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21064 this.cleanUpChild(n.childNodes[i]);
21071 cleanUpChild : function (node)
21074 //console.log(node);
21075 if (node.nodeName == "#text") {
21076 // clean up silly Windows -- stuff?
21079 if (node.nodeName == "#comment") {
21080 node.parentNode.removeChild(node);
21081 // clean up silly Windows -- stuff?
21084 var lcname = node.tagName.toLowerCase();
21085 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21086 // whitelist of tags..
21088 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21090 node.parentNode.removeChild(node);
21095 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21097 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21098 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21100 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21101 // remove_keep_children = true;
21104 if (remove_keep_children) {
21105 this.cleanUpChildren(node);
21106 // inserts everything just before this node...
21107 while (node.childNodes.length) {
21108 var cn = node.childNodes[0];
21109 node.removeChild(cn);
21110 node.parentNode.insertBefore(cn, node);
21112 node.parentNode.removeChild(node);
21116 if (!node.attributes || !node.attributes.length) {
21117 this.cleanUpChildren(node);
21121 function cleanAttr(n,v)
21124 if (v.match(/^\./) || v.match(/^\//)) {
21127 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21130 if (v.match(/^#/)) {
21133 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21134 node.removeAttribute(n);
21138 var cwhite = this.cwhite;
21139 var cblack = this.cblack;
21141 function cleanStyle(n,v)
21143 if (v.match(/expression/)) { //XSS?? should we even bother..
21144 node.removeAttribute(n);
21148 var parts = v.split(/;/);
21151 Roo.each(parts, function(p) {
21152 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21156 var l = p.split(':').shift().replace(/\s+/g,'');
21157 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21159 if ( cwhite.length && cblack.indexOf(l) > -1) {
21160 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21161 //node.removeAttribute(n);
21165 // only allow 'c whitelisted system attributes'
21166 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21167 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21168 //node.removeAttribute(n);
21178 if (clean.length) {
21179 node.setAttribute(n, clean.join(';'));
21181 node.removeAttribute(n);
21187 for (var i = node.attributes.length-1; i > -1 ; i--) {
21188 var a = node.attributes[i];
21191 if (a.name.toLowerCase().substr(0,2)=='on') {
21192 node.removeAttribute(a.name);
21195 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21196 node.removeAttribute(a.name);
21199 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21200 cleanAttr(a.name,a.value); // fixme..
21203 if (a.name == 'style') {
21204 cleanStyle(a.name,a.value);
21207 /// clean up MS crap..
21208 // tecnically this should be a list of valid class'es..
21211 if (a.name == 'class') {
21212 if (a.value.match(/^Mso/)) {
21213 node.className = '';
21216 if (a.value.match(/body/)) {
21217 node.className = '';
21228 this.cleanUpChildren(node);
21234 * Clean up MS wordisms...
21236 cleanWord : function(node)
21241 this.cleanWord(this.doc.body);
21244 if (node.nodeName == "#text") {
21245 // clean up silly Windows -- stuff?
21248 if (node.nodeName == "#comment") {
21249 node.parentNode.removeChild(node);
21250 // clean up silly Windows -- stuff?
21254 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21255 node.parentNode.removeChild(node);
21259 // remove - but keep children..
21260 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21261 while (node.childNodes.length) {
21262 var cn = node.childNodes[0];
21263 node.removeChild(cn);
21264 node.parentNode.insertBefore(cn, node);
21266 node.parentNode.removeChild(node);
21267 this.iterateChildren(node, this.cleanWord);
21271 if (node.className.length) {
21273 var cn = node.className.split(/\W+/);
21275 Roo.each(cn, function(cls) {
21276 if (cls.match(/Mso[a-zA-Z]+/)) {
21281 node.className = cna.length ? cna.join(' ') : '';
21283 node.removeAttribute("class");
21287 if (node.hasAttribute("lang")) {
21288 node.removeAttribute("lang");
21291 if (node.hasAttribute("style")) {
21293 var styles = node.getAttribute("style").split(";");
21295 Roo.each(styles, function(s) {
21296 if (!s.match(/:/)) {
21299 var kv = s.split(":");
21300 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21303 // what ever is left... we allow.
21306 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21307 if (!nstyle.length) {
21308 node.removeAttribute('style');
21311 this.iterateChildren(node, this.cleanWord);
21317 * iterateChildren of a Node, calling fn each time, using this as the scole..
21318 * @param {DomNode} node node to iterate children of.
21319 * @param {Function} fn method of this class to call on each item.
21321 iterateChildren : function(node, fn)
21323 if (!node.childNodes.length) {
21326 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21327 fn.call(this, node.childNodes[i])
21333 * cleanTableWidths.
21335 * Quite often pasting from word etc.. results in tables with column and widths.
21336 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21339 cleanTableWidths : function(node)
21344 this.cleanTableWidths(this.doc.body);
21349 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21352 Roo.log(node.tagName);
21353 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21354 this.iterateChildren(node, this.cleanTableWidths);
21357 if (node.hasAttribute('width')) {
21358 node.removeAttribute('width');
21362 if (node.hasAttribute("style")) {
21365 var styles = node.getAttribute("style").split(";");
21367 Roo.each(styles, function(s) {
21368 if (!s.match(/:/)) {
21371 var kv = s.split(":");
21372 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21375 // what ever is left... we allow.
21378 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21379 if (!nstyle.length) {
21380 node.removeAttribute('style');
21384 this.iterateChildren(node, this.cleanTableWidths);
21392 domToHTML : function(currentElement, depth, nopadtext) {
21394 depth = depth || 0;
21395 nopadtext = nopadtext || false;
21397 if (!currentElement) {
21398 return this.domToHTML(this.doc.body);
21401 //Roo.log(currentElement);
21403 var allText = false;
21404 var nodeName = currentElement.nodeName;
21405 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21407 if (nodeName == '#text') {
21409 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21414 if (nodeName != 'BODY') {
21417 // Prints the node tagName, such as <A>, <IMG>, etc
21420 for(i = 0; i < currentElement.attributes.length;i++) {
21422 var aname = currentElement.attributes.item(i).name;
21423 if (!currentElement.attributes.item(i).value.length) {
21426 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21429 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21438 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21441 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21446 // Traverse the tree
21448 var currentElementChild = currentElement.childNodes.item(i);
21449 var allText = true;
21450 var innerHTML = '';
21452 while (currentElementChild) {
21453 // Formatting code (indent the tree so it looks nice on the screen)
21454 var nopad = nopadtext;
21455 if (lastnode == 'SPAN') {
21459 if (currentElementChild.nodeName == '#text') {
21460 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21461 toadd = nopadtext ? toadd : toadd.trim();
21462 if (!nopad && toadd.length > 80) {
21463 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21465 innerHTML += toadd;
21468 currentElementChild = currentElement.childNodes.item(i);
21474 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21476 // Recursively traverse the tree structure of the child node
21477 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21478 lastnode = currentElementChild.nodeName;
21480 currentElementChild=currentElement.childNodes.item(i);
21486 // The remaining code is mostly for formatting the tree
21487 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21492 ret+= "</"+tagName+">";
21498 applyBlacklists : function()
21500 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21501 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21505 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21506 if (b.indexOf(tag) > -1) {
21509 this.white.push(tag);
21513 Roo.each(w, function(tag) {
21514 if (b.indexOf(tag) > -1) {
21517 if (this.white.indexOf(tag) > -1) {
21520 this.white.push(tag);
21525 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21526 if (w.indexOf(tag) > -1) {
21529 this.black.push(tag);
21533 Roo.each(b, function(tag) {
21534 if (w.indexOf(tag) > -1) {
21537 if (this.black.indexOf(tag) > -1) {
21540 this.black.push(tag);
21545 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21546 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21550 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21551 if (b.indexOf(tag) > -1) {
21554 this.cwhite.push(tag);
21558 Roo.each(w, function(tag) {
21559 if (b.indexOf(tag) > -1) {
21562 if (this.cwhite.indexOf(tag) > -1) {
21565 this.cwhite.push(tag);
21570 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21571 if (w.indexOf(tag) > -1) {
21574 this.cblack.push(tag);
21578 Roo.each(b, function(tag) {
21579 if (w.indexOf(tag) > -1) {
21582 if (this.cblack.indexOf(tag) > -1) {
21585 this.cblack.push(tag);
21590 setStylesheets : function(stylesheets)
21592 if(typeof(stylesheets) == 'string'){
21593 Roo.get(this.iframe.contentDocument.head).createChild({
21595 rel : 'stylesheet',
21604 Roo.each(stylesheets, function(s) {
21609 Roo.get(_this.iframe.contentDocument.head).createChild({
21611 rel : 'stylesheet',
21620 removeStylesheets : function()
21624 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21629 // hide stuff that is not compatible
21643 * @event specialkey
21647 * @cfg {String} fieldClass @hide
21650 * @cfg {String} focusClass @hide
21653 * @cfg {String} autoCreate @hide
21656 * @cfg {String} inputType @hide
21659 * @cfg {String} invalidClass @hide
21662 * @cfg {String} invalidText @hide
21665 * @cfg {String} msgFx @hide
21668 * @cfg {String} validateOnBlur @hide
21672 Roo.HtmlEditorCore.white = [
21673 'area', 'br', 'img', 'input', 'hr', 'wbr',
21675 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21676 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21677 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21678 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21679 'table', 'ul', 'xmp',
21681 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21684 'dir', 'menu', 'ol', 'ul', 'dl',
21690 Roo.HtmlEditorCore.black = [
21691 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21693 'base', 'basefont', 'bgsound', 'blink', 'body',
21694 'frame', 'frameset', 'head', 'html', 'ilayer',
21695 'iframe', 'layer', 'link', 'meta', 'object',
21696 'script', 'style' ,'title', 'xml' // clean later..
21698 Roo.HtmlEditorCore.clean = [
21699 'script', 'style', 'title', 'xml'
21701 Roo.HtmlEditorCore.remove = [
21706 Roo.HtmlEditorCore.ablack = [
21710 Roo.HtmlEditorCore.aclean = [
21711 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21715 Roo.HtmlEditorCore.pwhite= [
21716 'http', 'https', 'mailto'
21719 // white listed style attributes.
21720 Roo.HtmlEditorCore.cwhite= [
21721 // 'text-align', /// default is to allow most things..
21727 // black listed style attributes.
21728 Roo.HtmlEditorCore.cblack= [
21729 // 'font-size' -- this can be set by the project
21733 Roo.HtmlEditorCore.swapCodes =[
21752 * @class Roo.bootstrap.HtmlEditor
21753 * @extends Roo.bootstrap.TextArea
21754 * Bootstrap HtmlEditor class
21757 * Create a new HtmlEditor
21758 * @param {Object} config The config object
21761 Roo.bootstrap.HtmlEditor = function(config){
21762 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21763 if (!this.toolbars) {
21764 this.toolbars = [];
21766 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21769 * @event initialize
21770 * Fires when the editor is fully initialized (including the iframe)
21771 * @param {HtmlEditor} this
21776 * Fires when the editor is first receives the focus. Any insertion must wait
21777 * until after this event.
21778 * @param {HtmlEditor} this
21782 * @event beforesync
21783 * Fires before the textarea is updated with content from the editor iframe. Return false
21784 * to cancel the sync.
21785 * @param {HtmlEditor} this
21786 * @param {String} html
21790 * @event beforepush
21791 * Fires before the iframe editor is updated with content from the textarea. Return false
21792 * to cancel the push.
21793 * @param {HtmlEditor} this
21794 * @param {String} html
21799 * Fires when the textarea is updated with content from the editor iframe.
21800 * @param {HtmlEditor} this
21801 * @param {String} html
21806 * Fires when the iframe editor is updated with content from the textarea.
21807 * @param {HtmlEditor} this
21808 * @param {String} html
21812 * @event editmodechange
21813 * Fires when the editor switches edit modes
21814 * @param {HtmlEditor} this
21815 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21817 editmodechange: true,
21819 * @event editorevent
21820 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21821 * @param {HtmlEditor} this
21825 * @event firstfocus
21826 * Fires when on first focus - needed by toolbars..
21827 * @param {HtmlEditor} this
21832 * Auto save the htmlEditor value as a file into Events
21833 * @param {HtmlEditor} this
21837 * @event savedpreview
21838 * preview the saved version of htmlEditor
21839 * @param {HtmlEditor} this
21846 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21850 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21855 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21860 * @cfg {Number} height (in pixels)
21864 * @cfg {Number} width (in pixels)
21869 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21872 stylesheets: false,
21877 // private properties
21878 validationEvent : false,
21880 initialized : false,
21883 onFocus : Roo.emptyFn,
21885 hideMode:'offsets',
21888 tbContainer : false,
21890 toolbarContainer :function() {
21891 return this.wrap.select('.x-html-editor-tb',true).first();
21895 * Protected method that will not generally be called directly. It
21896 * is called when the editor creates its toolbar. Override this method if you need to
21897 * add custom toolbar buttons.
21898 * @param {HtmlEditor} editor
21900 createToolbar : function(){
21902 Roo.log("create toolbars");
21904 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21905 this.toolbars[0].render(this.toolbarContainer());
21909 // if (!editor.toolbars || !editor.toolbars.length) {
21910 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21913 // for (var i =0 ; i < editor.toolbars.length;i++) {
21914 // editor.toolbars[i] = Roo.factory(
21915 // typeof(editor.toolbars[i]) == 'string' ?
21916 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21917 // Roo.bootstrap.HtmlEditor);
21918 // editor.toolbars[i].init(editor);
21924 onRender : function(ct, position)
21926 // Roo.log("Call onRender: " + this.xtype);
21928 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21930 this.wrap = this.inputEl().wrap({
21931 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21934 this.editorcore.onRender(ct, position);
21936 if (this.resizable) {
21937 this.resizeEl = new Roo.Resizable(this.wrap, {
21941 minHeight : this.height,
21942 height: this.height,
21943 handles : this.resizable,
21946 resize : function(r, w, h) {
21947 _t.onResize(w,h); // -something
21953 this.createToolbar(this);
21956 if(!this.width && this.resizable){
21957 this.setSize(this.wrap.getSize());
21959 if (this.resizeEl) {
21960 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21961 // should trigger onReize..
21967 onResize : function(w, h)
21969 Roo.log('resize: ' +w + ',' + h );
21970 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21974 if(this.inputEl() ){
21975 if(typeof w == 'number'){
21976 var aw = w - this.wrap.getFrameWidth('lr');
21977 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21980 if(typeof h == 'number'){
21981 var tbh = -11; // fixme it needs to tool bar size!
21982 for (var i =0; i < this.toolbars.length;i++) {
21983 // fixme - ask toolbars for heights?
21984 tbh += this.toolbars[i].el.getHeight();
21985 //if (this.toolbars[i].footer) {
21986 // tbh += this.toolbars[i].footer.el.getHeight();
21994 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21995 ah -= 5; // knock a few pixes off for look..
21996 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22000 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22001 this.editorcore.onResize(ew,eh);
22006 * Toggles the editor between standard and source edit mode.
22007 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22009 toggleSourceEdit : function(sourceEditMode)
22011 this.editorcore.toggleSourceEdit(sourceEditMode);
22013 if(this.editorcore.sourceEditMode){
22014 Roo.log('editor - showing textarea');
22017 // Roo.log(this.syncValue());
22019 this.inputEl().removeClass(['hide', 'x-hidden']);
22020 this.inputEl().dom.removeAttribute('tabIndex');
22021 this.inputEl().focus();
22023 Roo.log('editor - hiding textarea');
22025 // Roo.log(this.pushValue());
22028 this.inputEl().addClass(['hide', 'x-hidden']);
22029 this.inputEl().dom.setAttribute('tabIndex', -1);
22030 //this.deferFocus();
22033 if(this.resizable){
22034 this.setSize(this.wrap.getSize());
22037 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22040 // private (for BoxComponent)
22041 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22043 // private (for BoxComponent)
22044 getResizeEl : function(){
22048 // private (for BoxComponent)
22049 getPositionEl : function(){
22054 initEvents : function(){
22055 this.originalValue = this.getValue();
22059 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22062 // markInvalid : Roo.emptyFn,
22064 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22067 // clearInvalid : Roo.emptyFn,
22069 setValue : function(v){
22070 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22071 this.editorcore.pushValue();
22076 deferFocus : function(){
22077 this.focus.defer(10, this);
22081 focus : function(){
22082 this.editorcore.focus();
22088 onDestroy : function(){
22094 for (var i =0; i < this.toolbars.length;i++) {
22095 // fixme - ask toolbars for heights?
22096 this.toolbars[i].onDestroy();
22099 this.wrap.dom.innerHTML = '';
22100 this.wrap.remove();
22105 onFirstFocus : function(){
22106 //Roo.log("onFirstFocus");
22107 this.editorcore.onFirstFocus();
22108 for (var i =0; i < this.toolbars.length;i++) {
22109 this.toolbars[i].onFirstFocus();
22115 syncValue : function()
22117 this.editorcore.syncValue();
22120 pushValue : function()
22122 this.editorcore.pushValue();
22126 // hide stuff that is not compatible
22140 * @event specialkey
22144 * @cfg {String} fieldClass @hide
22147 * @cfg {String} focusClass @hide
22150 * @cfg {String} autoCreate @hide
22153 * @cfg {String} inputType @hide
22156 * @cfg {String} invalidClass @hide
22159 * @cfg {String} invalidText @hide
22162 * @cfg {String} msgFx @hide
22165 * @cfg {String} validateOnBlur @hide
22174 Roo.namespace('Roo.bootstrap.htmleditor');
22176 * @class Roo.bootstrap.HtmlEditorToolbar1
22181 new Roo.bootstrap.HtmlEditor({
22184 new Roo.bootstrap.HtmlEditorToolbar1({
22185 disable : { fonts: 1 , format: 1, ..., ... , ...],
22191 * @cfg {Object} disable List of elements to disable..
22192 * @cfg {Array} btns List of additional buttons.
22196 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22199 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22202 Roo.apply(this, config);
22204 // default disabled, based on 'good practice'..
22205 this.disable = this.disable || {};
22206 Roo.applyIf(this.disable, {
22209 specialElements : true
22211 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22213 this.editor = config.editor;
22214 this.editorcore = config.editor.editorcore;
22216 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22218 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22219 // dont call parent... till later.
22221 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
22226 editorcore : false,
22231 "h1","h2","h3","h4","h5","h6",
22233 "abbr", "acronym", "address", "cite", "samp", "var",
22237 onRender : function(ct, position)
22239 // Roo.log("Call onRender: " + this.xtype);
22241 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22243 this.el.dom.style.marginBottom = '0';
22245 var editorcore = this.editorcore;
22246 var editor= this.editor;
22249 var btn = function(id,cmd , toggle, handler){
22251 var event = toggle ? 'toggle' : 'click';
22256 xns: Roo.bootstrap,
22259 enableToggle:toggle !== false,
22261 pressed : toggle ? false : null,
22264 a.listeners[toggle ? 'toggle' : 'click'] = function() {
22265 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
22274 xns: Roo.bootstrap,
22275 glyphicon : 'font',
22279 xns: Roo.bootstrap,
22283 Roo.each(this.formats, function(f) {
22284 style.menu.items.push({
22286 xns: Roo.bootstrap,
22287 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22292 editorcore.insertTag(this.tagname);
22299 children.push(style);
22302 btn('bold',false,true);
22303 btn('italic',false,true);
22304 btn('align-left', 'justifyleft',true);
22305 btn('align-center', 'justifycenter',true);
22306 btn('align-right' , 'justifyright',true);
22307 btn('link', false, false, function(btn) {
22308 //Roo.log("create link?");
22309 var url = prompt(this.createLinkText, this.defaultLinkValue);
22310 if(url && url != 'http:/'+'/'){
22311 this.editorcore.relayCmd('createlink', url);
22314 btn('list','insertunorderedlist',true);
22315 btn('pencil', false,true, function(btn){
22318 this.toggleSourceEdit(btn.pressed);
22324 xns: Roo.bootstrap,
22329 xns: Roo.bootstrap,
22334 cog.menu.items.push({
22336 xns: Roo.bootstrap,
22337 html : Clean styles,
22342 editorcore.insertTag(this.tagname);
22351 this.xtype = 'NavSimplebar';
22353 for(var i=0;i< children.length;i++) {
22355 this.buttons.add(this.addxtypeChild(children[i]));
22359 editor.on('editorevent', this.updateToolbar, this);
22361 onBtnClick : function(id)
22363 this.editorcore.relayCmd(id);
22364 this.editorcore.focus();
22368 * Protected method that will not generally be called directly. It triggers
22369 * a toolbar update by reading the markup state of the current selection in the editor.
22371 updateToolbar: function(){
22373 if(!this.editorcore.activated){
22374 this.editor.onFirstFocus(); // is this neeed?
22378 var btns = this.buttons;
22379 var doc = this.editorcore.doc;
22380 btns.get('bold').setActive(doc.queryCommandState('bold'));
22381 btns.get('italic').setActive(doc.queryCommandState('italic'));
22382 //btns.get('underline').setActive(doc.queryCommandState('underline'));
22384 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22385 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22386 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22388 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22389 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22392 var ans = this.editorcore.getAllAncestors();
22393 if (this.formatCombo) {
22396 var store = this.formatCombo.store;
22397 this.formatCombo.setValue("");
22398 for (var i =0; i < ans.length;i++) {
22399 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22401 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22409 // hides menus... - so this cant be on a menu...
22410 Roo.bootstrap.MenuMgr.hideAll();
22412 Roo.bootstrap.MenuMgr.hideAll();
22413 //this.editorsyncValue();
22415 onFirstFocus: function() {
22416 this.buttons.each(function(item){
22420 toggleSourceEdit : function(sourceEditMode){
22423 if(sourceEditMode){
22424 Roo.log("disabling buttons");
22425 this.buttons.each( function(item){
22426 if(item.cmd != 'pencil'){
22432 Roo.log("enabling buttons");
22433 if(this.editorcore.initialized){
22434 this.buttons.each( function(item){
22440 Roo.log("calling toggole on editor");
22441 // tell the editor that it's been pressed..
22442 this.editor.toggleSourceEdit(sourceEditMode);
22452 * @class Roo.bootstrap.Table.AbstractSelectionModel
22453 * @extends Roo.util.Observable
22454 * Abstract base class for grid SelectionModels. It provides the interface that should be
22455 * implemented by descendant classes. This class should not be directly instantiated.
22458 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22459 this.locked = false;
22460 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22464 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22465 /** @ignore Called by the grid automatically. Do not call directly. */
22466 init : function(grid){
22472 * Locks the selections.
22475 this.locked = true;
22479 * Unlocks the selections.
22481 unlock : function(){
22482 this.locked = false;
22486 * Returns true if the selections are locked.
22487 * @return {Boolean}
22489 isLocked : function(){
22490 return this.locked;
22494 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22495 * @class Roo.bootstrap.Table.RowSelectionModel
22496 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22497 * It supports multiple selections and keyboard selection/navigation.
22499 * @param {Object} config
22502 Roo.bootstrap.Table.RowSelectionModel = function(config){
22503 Roo.apply(this, config);
22504 this.selections = new Roo.util.MixedCollection(false, function(o){
22509 this.lastActive = false;
22513 * @event selectionchange
22514 * Fires when the selection changes
22515 * @param {SelectionModel} this
22517 "selectionchange" : true,
22519 * @event afterselectionchange
22520 * Fires after the selection changes (eg. by key press or clicking)
22521 * @param {SelectionModel} this
22523 "afterselectionchange" : true,
22525 * @event beforerowselect
22526 * Fires when a row is selected being selected, return false to cancel.
22527 * @param {SelectionModel} this
22528 * @param {Number} rowIndex The selected index
22529 * @param {Boolean} keepExisting False if other selections will be cleared
22531 "beforerowselect" : true,
22534 * Fires when a row is selected.
22535 * @param {SelectionModel} this
22536 * @param {Number} rowIndex The selected index
22537 * @param {Roo.data.Record} r The record
22539 "rowselect" : true,
22541 * @event rowdeselect
22542 * Fires when a row is deselected.
22543 * @param {SelectionModel} this
22544 * @param {Number} rowIndex The selected index
22546 "rowdeselect" : true
22548 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22549 this.locked = false;
22552 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22554 * @cfg {Boolean} singleSelect
22555 * True to allow selection of only one row at a time (defaults to false)
22557 singleSelect : false,
22560 initEvents : function()
22563 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22564 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
22565 //}else{ // allow click to work like normal
22566 // this.grid.on("rowclick", this.handleDragableRowClick, this);
22568 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22569 this.grid.on("rowclick", this.handleMouseDown, this);
22571 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22572 "up" : function(e){
22574 this.selectPrevious(e.shiftKey);
22575 }else if(this.last !== false && this.lastActive !== false){
22576 var last = this.last;
22577 this.selectRange(this.last, this.lastActive-1);
22578 this.grid.getView().focusRow(this.lastActive);
22579 if(last !== false){
22583 this.selectFirstRow();
22585 this.fireEvent("afterselectionchange", this);
22587 "down" : function(e){
22589 this.selectNext(e.shiftKey);
22590 }else if(this.last !== false && this.lastActive !== false){
22591 var last = this.last;
22592 this.selectRange(this.last, this.lastActive+1);
22593 this.grid.getView().focusRow(this.lastActive);
22594 if(last !== false){
22598 this.selectFirstRow();
22600 this.fireEvent("afterselectionchange", this);
22604 this.grid.store.on('load', function(){
22605 this.selections.clear();
22608 var view = this.grid.view;
22609 view.on("refresh", this.onRefresh, this);
22610 view.on("rowupdated", this.onRowUpdated, this);
22611 view.on("rowremoved", this.onRemove, this);
22616 onRefresh : function()
22618 var ds = this.grid.store, i, v = this.grid.view;
22619 var s = this.selections;
22620 s.each(function(r){
22621 if((i = ds.indexOfId(r.id)) != -1){
22630 onRemove : function(v, index, r){
22631 this.selections.remove(r);
22635 onRowUpdated : function(v, index, r){
22636 if(this.isSelected(r)){
22637 v.onRowSelect(index);
22643 * @param {Array} records The records to select
22644 * @param {Boolean} keepExisting (optional) True to keep existing selections
22646 selectRecords : function(records, keepExisting)
22649 this.clearSelections();
22651 var ds = this.grid.store;
22652 for(var i = 0, len = records.length; i < len; i++){
22653 this.selectRow(ds.indexOf(records[i]), true);
22658 * Gets the number of selected rows.
22661 getCount : function(){
22662 return this.selections.length;
22666 * Selects the first row in the grid.
22668 selectFirstRow : function(){
22673 * Select the last row.
22674 * @param {Boolean} keepExisting (optional) True to keep existing selections
22676 selectLastRow : function(keepExisting){
22677 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22678 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22682 * Selects the row immediately following the last selected row.
22683 * @param {Boolean} keepExisting (optional) True to keep existing selections
22685 selectNext : function(keepExisting)
22687 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22688 this.selectRow(this.last+1, keepExisting);
22689 this.grid.getView().focusRow(this.last);
22694 * Selects the row that precedes the last selected row.
22695 * @param {Boolean} keepExisting (optional) True to keep existing selections
22697 selectPrevious : function(keepExisting){
22699 this.selectRow(this.last-1, keepExisting);
22700 this.grid.getView().focusRow(this.last);
22705 * Returns the selected records
22706 * @return {Array} Array of selected records
22708 getSelections : function(){
22709 return [].concat(this.selections.items);
22713 * Returns the first selected record.
22716 getSelected : function(){
22717 return this.selections.itemAt(0);
22722 * Clears all selections.
22724 clearSelections : function(fast)
22730 var ds = this.grid.store;
22731 var s = this.selections;
22732 s.each(function(r){
22733 this.deselectRow(ds.indexOfId(r.id));
22737 this.selections.clear();
22744 * Selects all rows.
22746 selectAll : function(){
22750 this.selections.clear();
22751 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
22752 this.selectRow(i, true);
22757 * Returns True if there is a selection.
22758 * @return {Boolean}
22760 hasSelection : function(){
22761 return this.selections.length > 0;
22765 * Returns True if the specified row is selected.
22766 * @param {Number/Record} record The record or index of the record to check
22767 * @return {Boolean}
22769 isSelected : function(index){
22770 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
22771 return (r && this.selections.key(r.id) ? true : false);
22775 * Returns True if the specified record id is selected.
22776 * @param {String} id The id of record to check
22777 * @return {Boolean}
22779 isIdSelected : function(id){
22780 return (this.selections.key(id) ? true : false);
22785 handleMouseDBClick : function(e, t){
22789 handleMouseDown : function(e, t)
22791 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
22792 if(this.isLocked() || rowIndex < 0 ){
22795 if(e.shiftKey && this.last !== false){
22796 var last = this.last;
22797 this.selectRange(last, rowIndex, e.ctrlKey);
22798 this.last = last; // reset the last
22802 var isSelected = this.isSelected(rowIndex);
22803 //Roo.log("select row:" + rowIndex);
22805 this.deselectRow(rowIndex);
22807 this.selectRow(rowIndex, true);
22811 if(e.button !== 0 && isSelected){
22812 alert('rowIndex 2: ' + rowIndex);
22813 view.focusRow(rowIndex);
22814 }else if(e.ctrlKey && isSelected){
22815 this.deselectRow(rowIndex);
22816 }else if(!isSelected){
22817 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22818 view.focusRow(rowIndex);
22822 this.fireEvent("afterselectionchange", this);
22825 handleDragableRowClick : function(grid, rowIndex, e)
22827 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22828 this.selectRow(rowIndex, false);
22829 grid.view.focusRow(rowIndex);
22830 this.fireEvent("afterselectionchange", this);
22835 * Selects multiple rows.
22836 * @param {Array} rows Array of the indexes of the row to select
22837 * @param {Boolean} keepExisting (optional) True to keep existing selections
22839 selectRows : function(rows, keepExisting){
22841 this.clearSelections();
22843 for(var i = 0, len = rows.length; i < len; i++){
22844 this.selectRow(rows[i], true);
22849 * Selects a range of rows. All rows in between startRow and endRow are also selected.
22850 * @param {Number} startRow The index of the first row in the range
22851 * @param {Number} endRow The index of the last row in the range
22852 * @param {Boolean} keepExisting (optional) True to retain existing selections
22854 selectRange : function(startRow, endRow, keepExisting){
22859 this.clearSelections();
22861 if(startRow <= endRow){
22862 for(var i = startRow; i <= endRow; i++){
22863 this.selectRow(i, true);
22866 for(var i = startRow; i >= endRow; i--){
22867 this.selectRow(i, true);
22873 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22874 * @param {Number} startRow The index of the first row in the range
22875 * @param {Number} endRow The index of the last row in the range
22877 deselectRange : function(startRow, endRow, preventViewNotify){
22881 for(var i = startRow; i <= endRow; i++){
22882 this.deselectRow(i, preventViewNotify);
22888 * @param {Number} row The index of the row to select
22889 * @param {Boolean} keepExisting (optional) True to keep existing selections
22891 selectRow : function(index, keepExisting, preventViewNotify)
22893 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
22896 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22897 if(!keepExisting || this.singleSelect){
22898 this.clearSelections();
22901 var r = this.grid.store.getAt(index);
22902 //console.log('selectRow - record id :' + r.id);
22904 this.selections.add(r);
22905 this.last = this.lastActive = index;
22906 if(!preventViewNotify){
22907 var proxy = new Roo.Element(
22908 this.grid.getRowDom(index)
22910 proxy.addClass('bg-info info');
22912 this.fireEvent("rowselect", this, index, r);
22913 this.fireEvent("selectionchange", this);
22919 * @param {Number} row The index of the row to deselect
22921 deselectRow : function(index, preventViewNotify)
22926 if(this.last == index){
22929 if(this.lastActive == index){
22930 this.lastActive = false;
22933 var r = this.grid.store.getAt(index);
22938 this.selections.remove(r);
22939 //.console.log('deselectRow - record id :' + r.id);
22940 if(!preventViewNotify){
22942 var proxy = new Roo.Element(
22943 this.grid.getRowDom(index)
22945 proxy.removeClass('bg-info info');
22947 this.fireEvent("rowdeselect", this, index);
22948 this.fireEvent("selectionchange", this);
22952 restoreLast : function(){
22954 this.last = this._last;
22959 acceptsNav : function(row, col, cm){
22960 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22964 onEditorKey : function(field, e){
22965 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22970 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22972 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22974 }else if(k == e.ENTER && !e.ctrlKey){
22978 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22980 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22982 }else if(k == e.ESC){
22986 g.startEditing(newCell[0], newCell[1]);
22992 * Ext JS Library 1.1.1
22993 * Copyright(c) 2006-2007, Ext JS, LLC.
22995 * Originally Released Under LGPL - original licence link has changed is not relivant.
22998 * <script type="text/javascript">
23002 * @class Roo.bootstrap.PagingToolbar
23003 * @extends Roo.bootstrap.NavSimplebar
23004 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23006 * Create a new PagingToolbar
23007 * @param {Object} config The config object
23008 * @param {Roo.data.Store} store
23010 Roo.bootstrap.PagingToolbar = function(config)
23012 // old args format still supported... - xtype is prefered..
23013 // created from xtype...
23015 this.ds = config.dataSource;
23017 if (config.store && !this.ds) {
23018 this.store= Roo.factory(config.store, Roo.data);
23019 this.ds = this.store;
23020 this.ds.xmodule = this.xmodule || false;
23023 this.toolbarItems = [];
23024 if (config.items) {
23025 this.toolbarItems = config.items;
23028 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23033 this.bind(this.ds);
23036 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23040 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23042 * @cfg {Roo.data.Store} dataSource
23043 * The underlying data store providing the paged data
23046 * @cfg {String/HTMLElement/Element} container
23047 * container The id or element that will contain the toolbar
23050 * @cfg {Boolean} displayInfo
23051 * True to display the displayMsg (defaults to false)
23054 * @cfg {Number} pageSize
23055 * The number of records to display per page (defaults to 20)
23059 * @cfg {String} displayMsg
23060 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23062 displayMsg : 'Displaying {0} - {1} of {2}',
23064 * @cfg {String} emptyMsg
23065 * The message to display when no records are found (defaults to "No data to display")
23067 emptyMsg : 'No data to display',
23069 * Customizable piece of the default paging text (defaults to "Page")
23072 beforePageText : "Page",
23074 * Customizable piece of the default paging text (defaults to "of %0")
23077 afterPageText : "of {0}",
23079 * Customizable piece of the default paging text (defaults to "First Page")
23082 firstText : "First Page",
23084 * Customizable piece of the default paging text (defaults to "Previous Page")
23087 prevText : "Previous Page",
23089 * Customizable piece of the default paging text (defaults to "Next Page")
23092 nextText : "Next Page",
23094 * Customizable piece of the default paging text (defaults to "Last Page")
23097 lastText : "Last Page",
23099 * Customizable piece of the default paging text (defaults to "Refresh")
23102 refreshText : "Refresh",
23106 onRender : function(ct, position)
23108 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23109 this.navgroup.parentId = this.id;
23110 this.navgroup.onRender(this.el, null);
23111 // add the buttons to the navgroup
23113 if(this.displayInfo){
23114 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23115 this.displayEl = this.el.select('.x-paging-info', true).first();
23116 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23117 // this.displayEl = navel.el.select('span',true).first();
23123 Roo.each(_this.buttons, function(e){ // this might need to use render????
23124 Roo.factory(e).onRender(_this.el, null);
23128 Roo.each(_this.toolbarItems, function(e) {
23129 _this.navgroup.addItem(e);
23133 this.first = this.navgroup.addItem({
23134 tooltip: this.firstText,
23136 icon : 'fa fa-backward',
23138 preventDefault: true,
23139 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23142 this.prev = this.navgroup.addItem({
23143 tooltip: this.prevText,
23145 icon : 'fa fa-step-backward',
23147 preventDefault: true,
23148 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
23150 //this.addSeparator();
23153 var field = this.navgroup.addItem( {
23155 cls : 'x-paging-position',
23157 html : this.beforePageText +
23158 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23159 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
23162 this.field = field.el.select('input', true).first();
23163 this.field.on("keydown", this.onPagingKeydown, this);
23164 this.field.on("focus", function(){this.dom.select();});
23167 this.afterTextEl = field.el.select('.x-paging-after',true).first();
23168 //this.field.setHeight(18);
23169 //this.addSeparator();
23170 this.next = this.navgroup.addItem({
23171 tooltip: this.nextText,
23173 html : ' <i class="fa fa-step-forward">',
23175 preventDefault: true,
23176 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
23178 this.last = this.navgroup.addItem({
23179 tooltip: this.lastText,
23180 icon : 'fa fa-forward',
23183 preventDefault: true,
23184 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
23186 //this.addSeparator();
23187 this.loading = this.navgroup.addItem({
23188 tooltip: this.refreshText,
23189 icon: 'fa fa-refresh',
23190 preventDefault: true,
23191 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23197 updateInfo : function(){
23198 if(this.displayEl){
23199 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23200 var msg = count == 0 ?
23204 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
23206 this.displayEl.update(msg);
23211 onLoad : function(ds, r, o){
23212 this.cursor = o.params ? o.params.start : 0;
23213 var d = this.getPageData(),
23217 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23218 this.field.dom.value = ap;
23219 this.first.setDisabled(ap == 1);
23220 this.prev.setDisabled(ap == 1);
23221 this.next.setDisabled(ap == ps);
23222 this.last.setDisabled(ap == ps);
23223 this.loading.enable();
23228 getPageData : function(){
23229 var total = this.ds.getTotalCount();
23232 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23233 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23238 onLoadError : function(){
23239 this.loading.enable();
23243 onPagingKeydown : function(e){
23244 var k = e.getKey();
23245 var d = this.getPageData();
23247 var v = this.field.dom.value, pageNum;
23248 if(!v || isNaN(pageNum = parseInt(v, 10))){
23249 this.field.dom.value = d.activePage;
23252 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23253 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23256 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))
23258 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23259 this.field.dom.value = pageNum;
23260 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23263 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23265 var v = this.field.dom.value, pageNum;
23266 var increment = (e.shiftKey) ? 10 : 1;
23267 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23270 if(!v || isNaN(pageNum = parseInt(v, 10))) {
23271 this.field.dom.value = d.activePage;
23274 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23276 this.field.dom.value = parseInt(v, 10) + increment;
23277 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23278 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23285 beforeLoad : function(){
23287 this.loading.disable();
23292 onClick : function(which){
23301 ds.load({params:{start: 0, limit: this.pageSize}});
23304 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23307 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23310 var total = ds.getTotalCount();
23311 var extra = total % this.pageSize;
23312 var lastStart = extra ? (total - extra) : total-this.pageSize;
23313 ds.load({params:{start: lastStart, limit: this.pageSize}});
23316 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23322 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23323 * @param {Roo.data.Store} store The data store to unbind
23325 unbind : function(ds){
23326 ds.un("beforeload", this.beforeLoad, this);
23327 ds.un("load", this.onLoad, this);
23328 ds.un("loadexception", this.onLoadError, this);
23329 ds.un("remove", this.updateInfo, this);
23330 ds.un("add", this.updateInfo, this);
23331 this.ds = undefined;
23335 * Binds the paging toolbar to the specified {@link Roo.data.Store}
23336 * @param {Roo.data.Store} store The data store to bind
23338 bind : function(ds){
23339 ds.on("beforeload", this.beforeLoad, this);
23340 ds.on("load", this.onLoad, this);
23341 ds.on("loadexception", this.onLoadError, this);
23342 ds.on("remove", this.updateInfo, this);
23343 ds.on("add", this.updateInfo, this);
23354 * @class Roo.bootstrap.MessageBar
23355 * @extends Roo.bootstrap.Component
23356 * Bootstrap MessageBar class
23357 * @cfg {String} html contents of the MessageBar
23358 * @cfg {String} weight (info | success | warning | danger) default info
23359 * @cfg {String} beforeClass insert the bar before the given class
23360 * @cfg {Boolean} closable (true | false) default false
23361 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23364 * Create a new Element
23365 * @param {Object} config The config object
23368 Roo.bootstrap.MessageBar = function(config){
23369 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23372 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
23378 beforeClass: 'bootstrap-sticky-wrap',
23380 getAutoCreate : function(){
23384 cls: 'alert alert-dismissable alert-' + this.weight,
23389 html: this.html || ''
23395 cfg.cls += ' alert-messages-fixed';
23409 onRender : function(ct, position)
23411 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23414 var cfg = Roo.apply({}, this.getAutoCreate());
23418 cfg.cls += ' ' + this.cls;
23421 cfg.style = this.style;
23423 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23425 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23428 this.el.select('>button.close').on('click', this.hide, this);
23434 if (!this.rendered) {
23440 this.fireEvent('show', this);
23446 if (!this.rendered) {
23452 this.fireEvent('hide', this);
23455 update : function()
23457 // var e = this.el.dom.firstChild;
23459 // if(this.closable){
23460 // e = e.nextSibling;
23463 // e.data = this.html || '';
23465 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23481 * @class Roo.bootstrap.Graph
23482 * @extends Roo.bootstrap.Component
23483 * Bootstrap Graph class
23487 @cfg {String} graphtype bar | vbar | pie
23488 @cfg {number} g_x coodinator | centre x (pie)
23489 @cfg {number} g_y coodinator | centre y (pie)
23490 @cfg {number} g_r radius (pie)
23491 @cfg {number} g_height height of the chart (respected by all elements in the set)
23492 @cfg {number} g_width width of the chart (respected by all elements in the set)
23493 @cfg {Object} title The title of the chart
23496 -opts (object) options for the chart
23498 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23499 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23501 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.
23502 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23504 o stretch (boolean)
23506 -opts (object) options for the pie
23509 o startAngle (number)
23510 o endAngle (number)
23514 * Create a new Input
23515 * @param {Object} config The config object
23518 Roo.bootstrap.Graph = function(config){
23519 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23525 * The img click event for the img.
23526 * @param {Roo.EventObject} e
23532 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23543 //g_colors: this.colors,
23550 getAutoCreate : function(){
23561 onRender : function(ct,position){
23564 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23566 if (typeof(Raphael) == 'undefined') {
23567 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23571 this.raphael = Raphael(this.el.dom);
23573 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23574 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23575 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23576 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23578 r.text(160, 10, "Single Series Chart").attr(txtattr);
23579 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23580 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23581 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23583 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23584 r.barchart(330, 10, 300, 220, data1);
23585 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23586 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23589 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23590 // r.barchart(30, 30, 560, 250, xdata, {
23591 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23592 // axis : "0 0 1 1",
23593 // axisxlabels : xdata
23594 // //yvalues : cols,
23597 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23599 // this.load(null,xdata,{
23600 // axis : "0 0 1 1",
23601 // axisxlabels : xdata
23606 load : function(graphtype,xdata,opts)
23608 this.raphael.clear();
23610 graphtype = this.graphtype;
23615 var r = this.raphael,
23616 fin = function () {
23617 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23619 fout = function () {
23620 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23622 pfin = function() {
23623 this.sector.stop();
23624 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23627 this.label[0].stop();
23628 this.label[0].attr({ r: 7.5 });
23629 this.label[1].attr({ "font-weight": 800 });
23632 pfout = function() {
23633 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23636 this.label[0].animate({ r: 5 }, 500, "bounce");
23637 this.label[1].attr({ "font-weight": 400 });
23643 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23646 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23649 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23650 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23652 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23659 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23664 setTitle: function(o)
23669 initEvents: function() {
23672 this.el.on('click', this.onClick, this);
23676 onClick : function(e)
23678 Roo.log('img onclick');
23679 this.fireEvent('click', this, e);
23691 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23694 * @class Roo.bootstrap.dash.NumberBox
23695 * @extends Roo.bootstrap.Component
23696 * Bootstrap NumberBox class
23697 * @cfg {String} headline Box headline
23698 * @cfg {String} content Box content
23699 * @cfg {String} icon Box icon
23700 * @cfg {String} footer Footer text
23701 * @cfg {String} fhref Footer href
23704 * Create a new NumberBox
23705 * @param {Object} config The config object
23709 Roo.bootstrap.dash.NumberBox = function(config){
23710 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23714 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23723 getAutoCreate : function(){
23727 cls : 'small-box ',
23735 cls : 'roo-headline',
23736 html : this.headline
23740 cls : 'roo-content',
23741 html : this.content
23755 cls : 'ion ' + this.icon
23764 cls : 'small-box-footer',
23765 href : this.fhref || '#',
23769 cfg.cn.push(footer);
23776 onRender : function(ct,position){
23777 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23784 setHeadline: function (value)
23786 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23789 setFooter: function (value, href)
23791 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23794 this.el.select('a.small-box-footer',true).first().attr('href', href);
23799 setContent: function (value)
23801 this.el.select('.roo-content',true).first().dom.innerHTML = value;
23804 initEvents: function()
23818 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23821 * @class Roo.bootstrap.dash.TabBox
23822 * @extends Roo.bootstrap.Component
23823 * Bootstrap TabBox class
23824 * @cfg {String} title Title of the TabBox
23825 * @cfg {String} icon Icon of the TabBox
23826 * @cfg {Boolean} showtabs (true|false) show the tabs default true
23827 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23830 * Create a new TabBox
23831 * @param {Object} config The config object
23835 Roo.bootstrap.dash.TabBox = function(config){
23836 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23841 * When a pane is added
23842 * @param {Roo.bootstrap.dash.TabPane} pane
23846 * @event activatepane
23847 * When a pane is activated
23848 * @param {Roo.bootstrap.dash.TabPane} pane
23850 "activatepane" : true
23858 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
23863 tabScrollable : false,
23865 getChildContainer : function()
23867 return this.el.select('.tab-content', true).first();
23870 getAutoCreate : function(){
23874 cls: 'pull-left header',
23882 cls: 'fa ' + this.icon
23888 cls: 'nav nav-tabs pull-right',
23894 if(this.tabScrollable){
23901 cls: 'nav nav-tabs pull-right',
23912 cls: 'nav-tabs-custom',
23917 cls: 'tab-content no-padding',
23925 initEvents : function()
23927 //Roo.log('add add pane handler');
23928 this.on('addpane', this.onAddPane, this);
23931 * Updates the box title
23932 * @param {String} html to set the title to.
23934 setTitle : function(value)
23936 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23938 onAddPane : function(pane)
23940 this.panes.push(pane);
23941 //Roo.log('addpane');
23943 // tabs are rendere left to right..
23944 if(!this.showtabs){
23948 var ctr = this.el.select('.nav-tabs', true).first();
23951 var existing = ctr.select('.nav-tab',true);
23952 var qty = existing.getCount();;
23955 var tab = ctr.createChild({
23957 cls : 'nav-tab' + (qty ? '' : ' active'),
23965 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23968 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23970 pane.el.addClass('active');
23975 onTabClick : function(ev,un,ob,pane)
23977 //Roo.log('tab - prev default');
23978 ev.preventDefault();
23981 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23982 pane.tab.addClass('active');
23983 //Roo.log(pane.title);
23984 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23985 // technically we should have a deactivate event.. but maybe add later.
23986 // and it should not de-activate the selected tab...
23987 this.fireEvent('activatepane', pane);
23988 pane.el.addClass('active');
23989 pane.fireEvent('activate');
23994 getActivePane : function()
23997 Roo.each(this.panes, function(p) {
23998 if(p.el.hasClass('active')){
24019 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24021 * @class Roo.bootstrap.TabPane
24022 * @extends Roo.bootstrap.Component
24023 * Bootstrap TabPane class
24024 * @cfg {Boolean} active (false | true) Default false
24025 * @cfg {String} title title of panel
24029 * Create a new TabPane
24030 * @param {Object} config The config object
24033 Roo.bootstrap.dash.TabPane = function(config){
24034 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24040 * When a pane is activated
24041 * @param {Roo.bootstrap.dash.TabPane} pane
24048 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
24053 // the tabBox that this is attached to.
24056 getAutoCreate : function()
24064 cfg.cls += ' active';
24069 initEvents : function()
24071 //Roo.log('trigger add pane handler');
24072 this.parent().fireEvent('addpane', this)
24076 * Updates the tab title
24077 * @param {String} html to set the title to.
24079 setTitle: function(str)
24085 this.tab.select('a', true).first().dom.innerHTML = str;
24102 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24105 * @class Roo.bootstrap.menu.Menu
24106 * @extends Roo.bootstrap.Component
24107 * Bootstrap Menu class - container for Menu
24108 * @cfg {String} html Text of the menu
24109 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24110 * @cfg {String} icon Font awesome icon
24111 * @cfg {String} pos Menu align to (top | bottom) default bottom
24115 * Create a new Menu
24116 * @param {Object} config The config object
24120 Roo.bootstrap.menu.Menu = function(config){
24121 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24125 * @event beforeshow
24126 * Fires before this menu is displayed
24127 * @param {Roo.bootstrap.menu.Menu} this
24131 * @event beforehide
24132 * Fires before this menu is hidden
24133 * @param {Roo.bootstrap.menu.Menu} this
24138 * Fires after this menu is displayed
24139 * @param {Roo.bootstrap.menu.Menu} this
24144 * Fires after this menu is hidden
24145 * @param {Roo.bootstrap.menu.Menu} this
24150 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24151 * @param {Roo.bootstrap.menu.Menu} this
24152 * @param {Roo.EventObject} e
24159 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
24163 weight : 'default',
24168 getChildContainer : function() {
24169 if(this.isSubMenu){
24173 return this.el.select('ul.dropdown-menu', true).first();
24176 getAutoCreate : function()
24181 cls : 'roo-menu-text',
24189 cls : 'fa ' + this.icon
24200 cls : 'dropdown-button btn btn-' + this.weight,
24205 cls : 'dropdown-toggle btn btn-' + this.weight,
24215 cls : 'dropdown-menu'
24221 if(this.pos == 'top'){
24222 cfg.cls += ' dropup';
24225 if(this.isSubMenu){
24228 cls : 'dropdown-menu'
24235 onRender : function(ct, position)
24237 this.isSubMenu = ct.hasClass('dropdown-submenu');
24239 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24242 initEvents : function()
24244 if(this.isSubMenu){
24248 this.hidden = true;
24250 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24251 this.triggerEl.on('click', this.onTriggerPress, this);
24253 this.buttonEl = this.el.select('button.dropdown-button', true).first();
24254 this.buttonEl.on('click', this.onClick, this);
24260 if(this.isSubMenu){
24264 return this.el.select('ul.dropdown-menu', true).first();
24267 onClick : function(e)
24269 this.fireEvent("click", this, e);
24272 onTriggerPress : function(e)
24274 if (this.isVisible()) {
24281 isVisible : function(){
24282 return !this.hidden;
24287 this.fireEvent("beforeshow", this);
24289 this.hidden = false;
24290 this.el.addClass('open');
24292 Roo.get(document).on("mouseup", this.onMouseUp, this);
24294 this.fireEvent("show", this);
24301 this.fireEvent("beforehide", this);
24303 this.hidden = true;
24304 this.el.removeClass('open');
24306 Roo.get(document).un("mouseup", this.onMouseUp);
24308 this.fireEvent("hide", this);
24311 onMouseUp : function()
24325 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24328 * @class Roo.bootstrap.menu.Item
24329 * @extends Roo.bootstrap.Component
24330 * Bootstrap MenuItem class
24331 * @cfg {Boolean} submenu (true | false) default false
24332 * @cfg {String} html text of the item
24333 * @cfg {String} href the link
24334 * @cfg {Boolean} disable (true | false) default false
24335 * @cfg {Boolean} preventDefault (true | false) default true
24336 * @cfg {String} icon Font awesome icon
24337 * @cfg {String} pos Submenu align to (left | right) default right
24341 * Create a new Item
24342 * @param {Object} config The config object
24346 Roo.bootstrap.menu.Item = function(config){
24347 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24351 * Fires when the mouse is hovering over this menu
24352 * @param {Roo.bootstrap.menu.Item} this
24353 * @param {Roo.EventObject} e
24358 * Fires when the mouse exits this menu
24359 * @param {Roo.bootstrap.menu.Item} this
24360 * @param {Roo.EventObject} e
24366 * The raw click event for the entire grid.
24367 * @param {Roo.EventObject} e
24373 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
24378 preventDefault: true,
24383 getAutoCreate : function()
24388 cls : 'roo-menu-item-text',
24396 cls : 'fa ' + this.icon
24405 href : this.href || '#',
24412 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24416 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24418 if(this.pos == 'left'){
24419 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24426 initEvents : function()
24428 this.el.on('mouseover', this.onMouseOver, this);
24429 this.el.on('mouseout', this.onMouseOut, this);
24431 this.el.select('a', true).first().on('click', this.onClick, this);
24435 onClick : function(e)
24437 if(this.preventDefault){
24438 e.preventDefault();
24441 this.fireEvent("click", this, e);
24444 onMouseOver : function(e)
24446 if(this.submenu && this.pos == 'left'){
24447 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24450 this.fireEvent("mouseover", this, e);
24453 onMouseOut : function(e)
24455 this.fireEvent("mouseout", this, e);
24467 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24470 * @class Roo.bootstrap.menu.Separator
24471 * @extends Roo.bootstrap.Component
24472 * Bootstrap Separator class
24475 * Create a new Separator
24476 * @param {Object} config The config object
24480 Roo.bootstrap.menu.Separator = function(config){
24481 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24484 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24486 getAutoCreate : function(){
24507 * @class Roo.bootstrap.Tooltip
24508 * Bootstrap Tooltip class
24509 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24510 * to determine which dom element triggers the tooltip.
24512 * It needs to add support for additional attributes like tooltip-position
24515 * Create a new Toolti
24516 * @param {Object} config The config object
24519 Roo.bootstrap.Tooltip = function(config){
24520 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24523 Roo.apply(Roo.bootstrap.Tooltip, {
24525 * @function init initialize tooltip monitoring.
24529 currentTip : false,
24530 currentRegion : false,
24536 Roo.get(document).on('mouseover', this.enter ,this);
24537 Roo.get(document).on('mouseout', this.leave, this);
24540 this.currentTip = new Roo.bootstrap.Tooltip();
24543 enter : function(ev)
24545 var dom = ev.getTarget();
24547 //Roo.log(['enter',dom]);
24548 var el = Roo.fly(dom);
24549 if (this.currentEl) {
24551 //Roo.log(this.currentEl);
24552 //Roo.log(this.currentEl.contains(dom));
24553 if (this.currentEl == el) {
24556 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24562 if (this.currentTip.el) {
24563 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24567 if(!el || el.dom == document){
24573 // you can not look for children, as if el is the body.. then everythign is the child..
24574 if (!el.attr('tooltip')) { //
24575 if (!el.select("[tooltip]").elements.length) {
24578 // is the mouse over this child...?
24579 bindEl = el.select("[tooltip]").first();
24580 var xy = ev.getXY();
24581 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24582 //Roo.log("not in region.");
24585 //Roo.log("child element over..");
24588 this.currentEl = bindEl;
24589 this.currentTip.bind(bindEl);
24590 this.currentRegion = Roo.lib.Region.getRegion(dom);
24591 this.currentTip.enter();
24594 leave : function(ev)
24596 var dom = ev.getTarget();
24597 //Roo.log(['leave',dom]);
24598 if (!this.currentEl) {
24603 if (dom != this.currentEl.dom) {
24606 var xy = ev.getXY();
24607 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24610 // only activate leave if mouse cursor is outside... bounding box..
24615 if (this.currentTip) {
24616 this.currentTip.leave();
24618 //Roo.log('clear currentEl');
24619 this.currentEl = false;
24624 'left' : ['r-l', [-2,0], 'right'],
24625 'right' : ['l-r', [2,0], 'left'],
24626 'bottom' : ['t-b', [0,2], 'top'],
24627 'top' : [ 'b-t', [0,-2], 'bottom']
24633 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24638 delay : null, // can be { show : 300 , hide: 500}
24642 hoverState : null, //???
24644 placement : 'bottom',
24646 getAutoCreate : function(){
24653 cls : 'tooltip-arrow'
24656 cls : 'tooltip-inner'
24663 bind : function(el)
24669 enter : function () {
24671 if (this.timeout != null) {
24672 clearTimeout(this.timeout);
24675 this.hoverState = 'in';
24676 //Roo.log("enter - show");
24677 if (!this.delay || !this.delay.show) {
24682 this.timeout = setTimeout(function () {
24683 if (_t.hoverState == 'in') {
24686 }, this.delay.show);
24690 clearTimeout(this.timeout);
24692 this.hoverState = 'out';
24693 if (!this.delay || !this.delay.hide) {
24699 this.timeout = setTimeout(function () {
24700 //Roo.log("leave - timeout");
24702 if (_t.hoverState == 'out') {
24704 Roo.bootstrap.Tooltip.currentEl = false;
24712 this.render(document.body);
24715 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24717 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24719 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24721 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24723 var placement = typeof this.placement == 'function' ?
24724 this.placement.call(this, this.el, on_el) :
24727 var autoToken = /\s?auto?\s?/i;
24728 var autoPlace = autoToken.test(placement);
24730 placement = placement.replace(autoToken, '') || 'top';
24734 //this.el.setXY([0,0]);
24736 //this.el.dom.style.display='block';
24738 //this.el.appendTo(on_el);
24740 var p = this.getPosition();
24741 var box = this.el.getBox();
24747 var align = Roo.bootstrap.Tooltip.alignment[placement];
24749 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24751 if(placement == 'top' || placement == 'bottom'){
24753 placement = 'right';
24756 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24757 placement = 'left';
24760 var scroll = Roo.select('body', true).first().getScroll();
24762 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24768 align = Roo.bootstrap.Tooltip.alignment[placement];
24770 this.el.alignTo(this.bindEl, align[0],align[1]);
24771 //var arrow = this.el.select('.arrow',true).first();
24772 //arrow.set(align[2],
24774 this.el.addClass(placement);
24776 this.el.addClass('in fade');
24778 this.hoverState = null;
24780 if (this.el.hasClass('fade')) {
24791 //this.el.setXY([0,0]);
24792 this.el.removeClass('in');
24808 * @class Roo.bootstrap.LocationPicker
24809 * @extends Roo.bootstrap.Component
24810 * Bootstrap LocationPicker class
24811 * @cfg {Number} latitude Position when init default 0
24812 * @cfg {Number} longitude Position when init default 0
24813 * @cfg {Number} zoom default 15
24814 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24815 * @cfg {Boolean} mapTypeControl default false
24816 * @cfg {Boolean} disableDoubleClickZoom default false
24817 * @cfg {Boolean} scrollwheel default true
24818 * @cfg {Boolean} streetViewControl default false
24819 * @cfg {Number} radius default 0
24820 * @cfg {String} locationName
24821 * @cfg {Boolean} draggable default true
24822 * @cfg {Boolean} enableAutocomplete default false
24823 * @cfg {Boolean} enableReverseGeocode default true
24824 * @cfg {String} markerTitle
24827 * Create a new LocationPicker
24828 * @param {Object} config The config object
24832 Roo.bootstrap.LocationPicker = function(config){
24834 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24839 * Fires when the picker initialized.
24840 * @param {Roo.bootstrap.LocationPicker} this
24841 * @param {Google Location} location
24845 * @event positionchanged
24846 * Fires when the picker position changed.
24847 * @param {Roo.bootstrap.LocationPicker} this
24848 * @param {Google Location} location
24850 positionchanged : true,
24853 * Fires when the map resize.
24854 * @param {Roo.bootstrap.LocationPicker} this
24859 * Fires when the map show.
24860 * @param {Roo.bootstrap.LocationPicker} this
24865 * Fires when the map hide.
24866 * @param {Roo.bootstrap.LocationPicker} this
24871 * Fires when click the map.
24872 * @param {Roo.bootstrap.LocationPicker} this
24873 * @param {Map event} e
24877 * @event mapRightClick
24878 * Fires when right click the map.
24879 * @param {Roo.bootstrap.LocationPicker} this
24880 * @param {Map event} e
24882 mapRightClick : true,
24884 * @event markerClick
24885 * Fires when click the marker.
24886 * @param {Roo.bootstrap.LocationPicker} this
24887 * @param {Map event} e
24889 markerClick : true,
24891 * @event markerRightClick
24892 * Fires when right click the marker.
24893 * @param {Roo.bootstrap.LocationPicker} this
24894 * @param {Map event} e
24896 markerRightClick : true,
24898 * @event OverlayViewDraw
24899 * Fires when OverlayView Draw
24900 * @param {Roo.bootstrap.LocationPicker} this
24902 OverlayViewDraw : true,
24904 * @event OverlayViewOnAdd
24905 * Fires when OverlayView Draw
24906 * @param {Roo.bootstrap.LocationPicker} this
24908 OverlayViewOnAdd : true,
24910 * @event OverlayViewOnRemove
24911 * Fires when OverlayView Draw
24912 * @param {Roo.bootstrap.LocationPicker} this
24914 OverlayViewOnRemove : true,
24916 * @event OverlayViewShow
24917 * Fires when OverlayView Draw
24918 * @param {Roo.bootstrap.LocationPicker} this
24919 * @param {Pixel} cpx
24921 OverlayViewShow : true,
24923 * @event OverlayViewHide
24924 * Fires when OverlayView Draw
24925 * @param {Roo.bootstrap.LocationPicker} this
24927 OverlayViewHide : true,
24929 * @event loadexception
24930 * Fires when load google lib failed.
24931 * @param {Roo.bootstrap.LocationPicker} this
24933 loadexception : true
24938 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24940 gMapContext: false,
24946 mapTypeControl: false,
24947 disableDoubleClickZoom: false,
24949 streetViewControl: false,
24953 enableAutocomplete: false,
24954 enableReverseGeocode: true,
24957 getAutoCreate: function()
24962 cls: 'roo-location-picker'
24968 initEvents: function(ct, position)
24970 if(!this.el.getWidth() || this.isApplied()){
24974 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24979 initial: function()
24981 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24982 this.fireEvent('loadexception', this);
24986 if(!this.mapTypeId){
24987 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24990 this.gMapContext = this.GMapContext();
24992 this.initOverlayView();
24994 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24998 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24999 _this.setPosition(_this.gMapContext.marker.position);
25002 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25003 _this.fireEvent('mapClick', this, event);
25007 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25008 _this.fireEvent('mapRightClick', this, event);
25012 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25013 _this.fireEvent('markerClick', this, event);
25017 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25018 _this.fireEvent('markerRightClick', this, event);
25022 this.setPosition(this.gMapContext.location);
25024 this.fireEvent('initial', this, this.gMapContext.location);
25027 initOverlayView: function()
25031 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25035 _this.fireEvent('OverlayViewDraw', _this);
25040 _this.fireEvent('OverlayViewOnAdd', _this);
25043 onRemove: function()
25045 _this.fireEvent('OverlayViewOnRemove', _this);
25048 show: function(cpx)
25050 _this.fireEvent('OverlayViewShow', _this, cpx);
25055 _this.fireEvent('OverlayViewHide', _this);
25061 fromLatLngToContainerPixel: function(event)
25063 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25066 isApplied: function()
25068 return this.getGmapContext() == false ? false : true;
25071 getGmapContext: function()
25073 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25076 GMapContext: function()
25078 var position = new google.maps.LatLng(this.latitude, this.longitude);
25080 var _map = new google.maps.Map(this.el.dom, {
25083 mapTypeId: this.mapTypeId,
25084 mapTypeControl: this.mapTypeControl,
25085 disableDoubleClickZoom: this.disableDoubleClickZoom,
25086 scrollwheel: this.scrollwheel,
25087 streetViewControl: this.streetViewControl,
25088 locationName: this.locationName,
25089 draggable: this.draggable,
25090 enableAutocomplete: this.enableAutocomplete,
25091 enableReverseGeocode: this.enableReverseGeocode
25094 var _marker = new google.maps.Marker({
25095 position: position,
25097 title: this.markerTitle,
25098 draggable: this.draggable
25105 location: position,
25106 radius: this.radius,
25107 locationName: this.locationName,
25108 addressComponents: {
25109 formatted_address: null,
25110 addressLine1: null,
25111 addressLine2: null,
25113 streetNumber: null,
25117 stateOrProvince: null
25120 domContainer: this.el.dom,
25121 geodecoder: new google.maps.Geocoder()
25125 drawCircle: function(center, radius, options)
25127 if (this.gMapContext.circle != null) {
25128 this.gMapContext.circle.setMap(null);
25132 options = Roo.apply({}, options, {
25133 strokeColor: "#0000FF",
25134 strokeOpacity: .35,
25136 fillColor: "#0000FF",
25140 options.map = this.gMapContext.map;
25141 options.radius = radius;
25142 options.center = center;
25143 this.gMapContext.circle = new google.maps.Circle(options);
25144 return this.gMapContext.circle;
25150 setPosition: function(location)
25152 this.gMapContext.location = location;
25153 this.gMapContext.marker.setPosition(location);
25154 this.gMapContext.map.panTo(location);
25155 this.drawCircle(location, this.gMapContext.radius, {});
25159 if (this.gMapContext.settings.enableReverseGeocode) {
25160 this.gMapContext.geodecoder.geocode({
25161 latLng: this.gMapContext.location
25162 }, function(results, status) {
25164 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25165 _this.gMapContext.locationName = results[0].formatted_address;
25166 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25168 _this.fireEvent('positionchanged', this, location);
25175 this.fireEvent('positionchanged', this, location);
25180 google.maps.event.trigger(this.gMapContext.map, "resize");
25182 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25184 this.fireEvent('resize', this);
25187 setPositionByLatLng: function(latitude, longitude)
25189 this.setPosition(new google.maps.LatLng(latitude, longitude));
25192 getCurrentPosition: function()
25195 latitude: this.gMapContext.location.lat(),
25196 longitude: this.gMapContext.location.lng()
25200 getAddressName: function()
25202 return this.gMapContext.locationName;
25205 getAddressComponents: function()
25207 return this.gMapContext.addressComponents;
25210 address_component_from_google_geocode: function(address_components)
25214 for (var i = 0; i < address_components.length; i++) {
25215 var component = address_components[i];
25216 if (component.types.indexOf("postal_code") >= 0) {
25217 result.postalCode = component.short_name;
25218 } else if (component.types.indexOf("street_number") >= 0) {
25219 result.streetNumber = component.short_name;
25220 } else if (component.types.indexOf("route") >= 0) {
25221 result.streetName = component.short_name;
25222 } else if (component.types.indexOf("neighborhood") >= 0) {
25223 result.city = component.short_name;
25224 } else if (component.types.indexOf("locality") >= 0) {
25225 result.city = component.short_name;
25226 } else if (component.types.indexOf("sublocality") >= 0) {
25227 result.district = component.short_name;
25228 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25229 result.stateOrProvince = component.short_name;
25230 } else if (component.types.indexOf("country") >= 0) {
25231 result.country = component.short_name;
25235 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25236 result.addressLine2 = "";
25240 setZoomLevel: function(zoom)
25242 this.gMapContext.map.setZoom(zoom);
25255 this.fireEvent('show', this);
25266 this.fireEvent('hide', this);
25271 Roo.apply(Roo.bootstrap.LocationPicker, {
25273 OverlayView : function(map, options)
25275 options = options || {};
25289 * @class Roo.bootstrap.Alert
25290 * @extends Roo.bootstrap.Component
25291 * Bootstrap Alert class
25292 * @cfg {String} title The title of alert
25293 * @cfg {String} html The content of alert
25294 * @cfg {String} weight ( success | info | warning | danger )
25295 * @cfg {String} faicon font-awesomeicon
25298 * Create a new alert
25299 * @param {Object} config The config object
25303 Roo.bootstrap.Alert = function(config){
25304 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25308 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
25315 getAutoCreate : function()
25324 cls : 'roo-alert-icon'
25329 cls : 'roo-alert-title',
25334 cls : 'roo-alert-text',
25341 cfg.cn[0].cls += ' fa ' + this.faicon;
25345 cfg.cls += ' alert-' + this.weight;
25351 initEvents: function()
25353 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25356 setTitle : function(str)
25358 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25361 setText : function(str)
25363 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25366 setWeight : function(weight)
25369 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25372 this.weight = weight;
25374 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25377 setIcon : function(icon)
25380 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25383 this.faicon = icon;
25385 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25406 * @class Roo.bootstrap.UploadCropbox
25407 * @extends Roo.bootstrap.Component
25408 * Bootstrap UploadCropbox class
25409 * @cfg {String} emptyText show when image has been loaded
25410 * @cfg {String} rotateNotify show when image too small to rotate
25411 * @cfg {Number} errorTimeout default 3000
25412 * @cfg {Number} minWidth default 300
25413 * @cfg {Number} minHeight default 300
25414 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25415 * @cfg {Boolean} isDocument (true|false) default false
25416 * @cfg {String} url action url
25417 * @cfg {String} paramName default 'imageUpload'
25418 * @cfg {String} method default POST
25419 * @cfg {Boolean} loadMask (true|false) default true
25420 * @cfg {Boolean} loadingText default 'Loading...'
25423 * Create a new UploadCropbox
25424 * @param {Object} config The config object
25427 Roo.bootstrap.UploadCropbox = function(config){
25428 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25432 * @event beforeselectfile
25433 * Fire before select file
25434 * @param {Roo.bootstrap.UploadCropbox} this
25436 "beforeselectfile" : true,
25439 * Fire after initEvent
25440 * @param {Roo.bootstrap.UploadCropbox} this
25445 * Fire after initEvent
25446 * @param {Roo.bootstrap.UploadCropbox} this
25447 * @param {String} data
25452 * Fire when preparing the file data
25453 * @param {Roo.bootstrap.UploadCropbox} this
25454 * @param {Object} file
25459 * Fire when get exception
25460 * @param {Roo.bootstrap.UploadCropbox} this
25461 * @param {XMLHttpRequest} xhr
25463 "exception" : true,
25465 * @event beforeloadcanvas
25466 * Fire before load the canvas
25467 * @param {Roo.bootstrap.UploadCropbox} this
25468 * @param {String} src
25470 "beforeloadcanvas" : true,
25473 * Fire when trash image
25474 * @param {Roo.bootstrap.UploadCropbox} this
25479 * Fire when download the image
25480 * @param {Roo.bootstrap.UploadCropbox} this
25484 * @event footerbuttonclick
25485 * Fire when footerbuttonclick
25486 * @param {Roo.bootstrap.UploadCropbox} this
25487 * @param {String} type
25489 "footerbuttonclick" : true,
25493 * @param {Roo.bootstrap.UploadCropbox} this
25498 * Fire when rotate the image
25499 * @param {Roo.bootstrap.UploadCropbox} this
25500 * @param {String} pos
25505 * Fire when inspect the file
25506 * @param {Roo.bootstrap.UploadCropbox} this
25507 * @param {Object} file
25512 * Fire when xhr upload the file
25513 * @param {Roo.bootstrap.UploadCropbox} this
25514 * @param {Object} data
25519 * Fire when arrange the file data
25520 * @param {Roo.bootstrap.UploadCropbox} this
25521 * @param {Object} formData
25526 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25529 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25531 emptyText : 'Click to upload image',
25532 rotateNotify : 'Image is too small to rotate',
25533 errorTimeout : 3000,
25547 cropType : 'image/jpeg',
25549 canvasLoaded : false,
25550 isDocument : false,
25552 paramName : 'imageUpload',
25554 loadingText : 'Loading...',
25557 getAutoCreate : function()
25561 cls : 'roo-upload-cropbox',
25565 cls : 'roo-upload-cropbox-selector',
25570 cls : 'roo-upload-cropbox-body',
25571 style : 'cursor:pointer',
25575 cls : 'roo-upload-cropbox-preview'
25579 cls : 'roo-upload-cropbox-thumb'
25583 cls : 'roo-upload-cropbox-empty-notify',
25584 html : this.emptyText
25588 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25589 html : this.rotateNotify
25595 cls : 'roo-upload-cropbox-footer',
25598 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25608 onRender : function(ct, position)
25610 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25612 if (this.buttons.length) {
25614 Roo.each(this.buttons, function(bb) {
25616 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25618 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25624 this.maskEl = this.el;
25628 initEvents : function()
25630 this.urlAPI = (window.createObjectURL && window) ||
25631 (window.URL && URL.revokeObjectURL && URL) ||
25632 (window.webkitURL && webkitURL);
25634 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25635 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25637 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25638 this.selectorEl.hide();
25640 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25641 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25643 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25644 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25645 this.thumbEl.hide();
25647 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25648 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25650 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25651 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25652 this.errorEl.hide();
25654 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25655 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25656 this.footerEl.hide();
25658 this.setThumbBoxSize();
25664 this.fireEvent('initial', this);
25671 window.addEventListener("resize", function() { _this.resize(); } );
25673 this.bodyEl.on('click', this.beforeSelectFile, this);
25676 this.bodyEl.on('touchstart', this.onTouchStart, this);
25677 this.bodyEl.on('touchmove', this.onTouchMove, this);
25678 this.bodyEl.on('touchend', this.onTouchEnd, this);
25682 this.bodyEl.on('mousedown', this.onMouseDown, this);
25683 this.bodyEl.on('mousemove', this.onMouseMove, this);
25684 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25685 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25686 Roo.get(document).on('mouseup', this.onMouseUp, this);
25689 this.selectorEl.on('change', this.onFileSelected, this);
25695 this.baseScale = 1;
25697 this.baseRotate = 1;
25698 this.dragable = false;
25699 this.pinching = false;
25702 this.cropData = false;
25703 this.notifyEl.dom.innerHTML = this.emptyText;
25705 this.selectorEl.dom.value = '';
25709 resize : function()
25711 if(this.fireEvent('resize', this) != false){
25712 this.setThumbBoxPosition();
25713 this.setCanvasPosition();
25717 onFooterButtonClick : function(e, el, o, type)
25720 case 'rotate-left' :
25721 this.onRotateLeft(e);
25723 case 'rotate-right' :
25724 this.onRotateRight(e);
25727 this.beforeSelectFile(e);
25742 this.fireEvent('footerbuttonclick', this, type);
25745 beforeSelectFile : function(e)
25747 e.preventDefault();
25749 if(this.fireEvent('beforeselectfile', this) != false){
25750 this.selectorEl.dom.click();
25754 onFileSelected : function(e)
25756 e.preventDefault();
25758 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25762 var file = this.selectorEl.dom.files[0];
25764 if(this.fireEvent('inspect', this, file) != false){
25765 this.prepare(file);
25770 trash : function(e)
25772 this.fireEvent('trash', this);
25775 download : function(e)
25777 this.fireEvent('download', this);
25780 loadCanvas : function(src)
25782 if(this.fireEvent('beforeloadcanvas', this, src) != false){
25786 this.imageEl = document.createElement('img');
25790 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25792 this.imageEl.src = src;
25796 onLoadCanvas : function()
25798 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25799 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25801 this.bodyEl.un('click', this.beforeSelectFile, this);
25803 this.notifyEl.hide();
25804 this.thumbEl.show();
25805 this.footerEl.show();
25807 this.baseRotateLevel();
25809 if(this.isDocument){
25810 this.setThumbBoxSize();
25813 this.setThumbBoxPosition();
25815 this.baseScaleLevel();
25821 this.canvasLoaded = true;
25824 this.maskEl.unmask();
25829 setCanvasPosition : function()
25831 if(!this.canvasEl){
25835 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25836 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25838 this.previewEl.setLeft(pw);
25839 this.previewEl.setTop(ph);
25843 onMouseDown : function(e)
25847 this.dragable = true;
25848 this.pinching = false;
25850 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25851 this.dragable = false;
25855 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25856 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25860 onMouseMove : function(e)
25864 if(!this.canvasLoaded){
25868 if (!this.dragable){
25872 var minX = Math.ceil(this.thumbEl.getLeft(true));
25873 var minY = Math.ceil(this.thumbEl.getTop(true));
25875 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25876 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25878 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25879 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25881 x = x - this.mouseX;
25882 y = y - this.mouseY;
25884 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25885 var bgY = Math.ceil(y + this.previewEl.getTop(true));
25887 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25888 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25890 this.previewEl.setLeft(bgX);
25891 this.previewEl.setTop(bgY);
25893 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25894 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25897 onMouseUp : function(e)
25901 this.dragable = false;
25904 onMouseWheel : function(e)
25908 this.startScale = this.scale;
25910 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25912 if(!this.zoomable()){
25913 this.scale = this.startScale;
25922 zoomable : function()
25924 var minScale = this.thumbEl.getWidth() / this.minWidth;
25926 if(this.minWidth < this.minHeight){
25927 minScale = this.thumbEl.getHeight() / this.minHeight;
25930 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25931 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25935 (this.rotate == 0 || this.rotate == 180) &&
25937 width > this.imageEl.OriginWidth ||
25938 height > this.imageEl.OriginHeight ||
25939 (width < this.minWidth && height < this.minHeight)
25947 (this.rotate == 90 || this.rotate == 270) &&
25949 width > this.imageEl.OriginWidth ||
25950 height > this.imageEl.OriginHeight ||
25951 (width < this.minHeight && height < this.minWidth)
25958 !this.isDocument &&
25959 (this.rotate == 0 || this.rotate == 180) &&
25961 width < this.minWidth ||
25962 width > this.imageEl.OriginWidth ||
25963 height < this.minHeight ||
25964 height > this.imageEl.OriginHeight
25971 !this.isDocument &&
25972 (this.rotate == 90 || this.rotate == 270) &&
25974 width < this.minHeight ||
25975 width > this.imageEl.OriginWidth ||
25976 height < this.minWidth ||
25977 height > this.imageEl.OriginHeight
25987 onRotateLeft : function(e)
25989 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25991 var minScale = this.thumbEl.getWidth() / this.minWidth;
25993 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25994 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25996 this.startScale = this.scale;
25998 while (this.getScaleLevel() < minScale){
26000 this.scale = this.scale + 1;
26002 if(!this.zoomable()){
26007 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26008 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26013 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26020 this.scale = this.startScale;
26022 this.onRotateFail();
26027 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26029 if(this.isDocument){
26030 this.setThumbBoxSize();
26031 this.setThumbBoxPosition();
26032 this.setCanvasPosition();
26037 this.fireEvent('rotate', this, 'left');
26041 onRotateRight : function(e)
26043 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26045 var minScale = this.thumbEl.getWidth() / this.minWidth;
26047 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26048 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26050 this.startScale = this.scale;
26052 while (this.getScaleLevel() < minScale){
26054 this.scale = this.scale + 1;
26056 if(!this.zoomable()){
26061 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26062 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26067 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26074 this.scale = this.startScale;
26076 this.onRotateFail();
26081 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26083 if(this.isDocument){
26084 this.setThumbBoxSize();
26085 this.setThumbBoxPosition();
26086 this.setCanvasPosition();
26091 this.fireEvent('rotate', this, 'right');
26094 onRotateFail : function()
26096 this.errorEl.show(true);
26100 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26105 this.previewEl.dom.innerHTML = '';
26107 var canvasEl = document.createElement("canvas");
26109 var contextEl = canvasEl.getContext("2d");
26111 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26112 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26113 var center = this.imageEl.OriginWidth / 2;
26115 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26116 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26117 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26118 center = this.imageEl.OriginHeight / 2;
26121 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26123 contextEl.translate(center, center);
26124 contextEl.rotate(this.rotate * Math.PI / 180);
26126 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26128 this.canvasEl = document.createElement("canvas");
26130 this.contextEl = this.canvasEl.getContext("2d");
26132 switch (this.rotate) {
26135 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26136 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26138 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26143 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26144 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26146 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26147 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26151 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26156 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26157 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26159 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26160 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);
26164 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);
26169 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26170 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26172 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26173 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26177 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);
26184 this.previewEl.appendChild(this.canvasEl);
26186 this.setCanvasPosition();
26191 if(!this.canvasLoaded){
26195 var imageCanvas = document.createElement("canvas");
26197 var imageContext = imageCanvas.getContext("2d");
26199 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26200 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26202 var center = imageCanvas.width / 2;
26204 imageContext.translate(center, center);
26206 imageContext.rotate(this.rotate * Math.PI / 180);
26208 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26210 var canvas = document.createElement("canvas");
26212 var context = canvas.getContext("2d");
26214 canvas.width = this.minWidth;
26215 canvas.height = this.minHeight;
26217 switch (this.rotate) {
26220 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26221 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26223 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26224 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26226 var targetWidth = this.minWidth - 2 * x;
26227 var targetHeight = this.minHeight - 2 * y;
26231 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26232 scale = targetWidth / width;
26235 if(x > 0 && y == 0){
26236 scale = targetHeight / height;
26239 if(x > 0 && y > 0){
26240 scale = targetWidth / width;
26242 if(width < height){
26243 scale = targetHeight / height;
26247 context.scale(scale, scale);
26249 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26250 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26252 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26253 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26255 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26260 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26261 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26263 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26264 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26266 var targetWidth = this.minWidth - 2 * x;
26267 var targetHeight = this.minHeight - 2 * y;
26271 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26272 scale = targetWidth / width;
26275 if(x > 0 && y == 0){
26276 scale = targetHeight / height;
26279 if(x > 0 && y > 0){
26280 scale = targetWidth / width;
26282 if(width < height){
26283 scale = targetHeight / height;
26287 context.scale(scale, scale);
26289 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26290 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26292 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26293 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26295 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26297 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26302 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26303 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26305 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26306 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26308 var targetWidth = this.minWidth - 2 * x;
26309 var targetHeight = this.minHeight - 2 * y;
26313 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26314 scale = targetWidth / width;
26317 if(x > 0 && y == 0){
26318 scale = targetHeight / height;
26321 if(x > 0 && y > 0){
26322 scale = targetWidth / width;
26324 if(width < height){
26325 scale = targetHeight / height;
26329 context.scale(scale, scale);
26331 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26332 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26334 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26335 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26337 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26338 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26340 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26345 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26346 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26348 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26349 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26351 var targetWidth = this.minWidth - 2 * x;
26352 var targetHeight = this.minHeight - 2 * y;
26356 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26357 scale = targetWidth / width;
26360 if(x > 0 && y == 0){
26361 scale = targetHeight / height;
26364 if(x > 0 && y > 0){
26365 scale = targetWidth / width;
26367 if(width < height){
26368 scale = targetHeight / height;
26372 context.scale(scale, scale);
26374 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26375 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26377 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26378 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26380 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26382 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26389 this.cropData = canvas.toDataURL(this.cropType);
26391 if(this.fireEvent('crop', this, this.cropData) !== false){
26392 this.process(this.file, this.cropData);
26399 setThumbBoxSize : function()
26403 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26404 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26405 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26407 this.minWidth = width;
26408 this.minHeight = height;
26410 if(this.rotate == 90 || this.rotate == 270){
26411 this.minWidth = height;
26412 this.minHeight = width;
26417 width = Math.ceil(this.minWidth * height / this.minHeight);
26419 if(this.minWidth > this.minHeight){
26421 height = Math.ceil(this.minHeight * width / this.minWidth);
26424 this.thumbEl.setStyle({
26425 width : width + 'px',
26426 height : height + 'px'
26433 setThumbBoxPosition : function()
26435 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26436 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26438 this.thumbEl.setLeft(x);
26439 this.thumbEl.setTop(y);
26443 baseRotateLevel : function()
26445 this.baseRotate = 1;
26448 typeof(this.exif) != 'undefined' &&
26449 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26450 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26452 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26455 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26459 baseScaleLevel : function()
26463 if(this.isDocument){
26465 if(this.baseRotate == 6 || this.baseRotate == 8){
26467 height = this.thumbEl.getHeight();
26468 this.baseScale = height / this.imageEl.OriginWidth;
26470 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26471 width = this.thumbEl.getWidth();
26472 this.baseScale = width / this.imageEl.OriginHeight;
26478 height = this.thumbEl.getHeight();
26479 this.baseScale = height / this.imageEl.OriginHeight;
26481 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26482 width = this.thumbEl.getWidth();
26483 this.baseScale = width / this.imageEl.OriginWidth;
26489 if(this.baseRotate == 6 || this.baseRotate == 8){
26491 width = this.thumbEl.getHeight();
26492 this.baseScale = width / this.imageEl.OriginHeight;
26494 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26495 height = this.thumbEl.getWidth();
26496 this.baseScale = height / this.imageEl.OriginHeight;
26499 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26500 height = this.thumbEl.getWidth();
26501 this.baseScale = height / this.imageEl.OriginHeight;
26503 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26504 width = this.thumbEl.getHeight();
26505 this.baseScale = width / this.imageEl.OriginWidth;
26512 width = this.thumbEl.getWidth();
26513 this.baseScale = width / this.imageEl.OriginWidth;
26515 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26516 height = this.thumbEl.getHeight();
26517 this.baseScale = height / this.imageEl.OriginHeight;
26520 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26522 height = this.thumbEl.getHeight();
26523 this.baseScale = height / this.imageEl.OriginHeight;
26525 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26526 width = this.thumbEl.getWidth();
26527 this.baseScale = width / this.imageEl.OriginWidth;
26535 getScaleLevel : function()
26537 return this.baseScale * Math.pow(1.1, this.scale);
26540 onTouchStart : function(e)
26542 if(!this.canvasLoaded){
26543 this.beforeSelectFile(e);
26547 var touches = e.browserEvent.touches;
26553 if(touches.length == 1){
26554 this.onMouseDown(e);
26558 if(touches.length != 2){
26564 for(var i = 0, finger; finger = touches[i]; i++){
26565 coords.push(finger.pageX, finger.pageY);
26568 var x = Math.pow(coords[0] - coords[2], 2);
26569 var y = Math.pow(coords[1] - coords[3], 2);
26571 this.startDistance = Math.sqrt(x + y);
26573 this.startScale = this.scale;
26575 this.pinching = true;
26576 this.dragable = false;
26580 onTouchMove : function(e)
26582 if(!this.pinching && !this.dragable){
26586 var touches = e.browserEvent.touches;
26593 this.onMouseMove(e);
26599 for(var i = 0, finger; finger = touches[i]; i++){
26600 coords.push(finger.pageX, finger.pageY);
26603 var x = Math.pow(coords[0] - coords[2], 2);
26604 var y = Math.pow(coords[1] - coords[3], 2);
26606 this.endDistance = Math.sqrt(x + y);
26608 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26610 if(!this.zoomable()){
26611 this.scale = this.startScale;
26619 onTouchEnd : function(e)
26621 this.pinching = false;
26622 this.dragable = false;
26626 process : function(file, crop)
26629 this.maskEl.mask(this.loadingText);
26632 this.xhr = new XMLHttpRequest();
26634 file.xhr = this.xhr;
26636 this.xhr.open(this.method, this.url, true);
26639 "Accept": "application/json",
26640 "Cache-Control": "no-cache",
26641 "X-Requested-With": "XMLHttpRequest"
26644 for (var headerName in headers) {
26645 var headerValue = headers[headerName];
26647 this.xhr.setRequestHeader(headerName, headerValue);
26653 this.xhr.onload = function()
26655 _this.xhrOnLoad(_this.xhr);
26658 this.xhr.onerror = function()
26660 _this.xhrOnError(_this.xhr);
26663 var formData = new FormData();
26665 formData.append('returnHTML', 'NO');
26668 formData.append('crop', crop);
26671 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26672 formData.append(this.paramName, file, file.name);
26675 if(typeof(file.filename) != 'undefined'){
26676 formData.append('filename', file.filename);
26679 if(typeof(file.mimetype) != 'undefined'){
26680 formData.append('mimetype', file.mimetype);
26683 if(this.fireEvent('arrange', this, formData) != false){
26684 this.xhr.send(formData);
26688 xhrOnLoad : function(xhr)
26691 this.maskEl.unmask();
26694 if (xhr.readyState !== 4) {
26695 this.fireEvent('exception', this, xhr);
26699 var response = Roo.decode(xhr.responseText);
26701 if(!response.success){
26702 this.fireEvent('exception', this, xhr);
26706 var response = Roo.decode(xhr.responseText);
26708 this.fireEvent('upload', this, response);
26712 xhrOnError : function()
26715 this.maskEl.unmask();
26718 Roo.log('xhr on error');
26720 var response = Roo.decode(xhr.responseText);
26726 prepare : function(file)
26729 this.maskEl.mask(this.loadingText);
26735 if(typeof(file) === 'string'){
26736 this.loadCanvas(file);
26740 if(!file || !this.urlAPI){
26745 this.cropType = file.type;
26749 if(this.fireEvent('prepare', this, this.file) != false){
26751 var reader = new FileReader();
26753 reader.onload = function (e) {
26754 if (e.target.error) {
26755 Roo.log(e.target.error);
26759 var buffer = e.target.result,
26760 dataView = new DataView(buffer),
26762 maxOffset = dataView.byteLength - 4,
26766 if (dataView.getUint16(0) === 0xffd8) {
26767 while (offset < maxOffset) {
26768 markerBytes = dataView.getUint16(offset);
26770 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26771 markerLength = dataView.getUint16(offset + 2) + 2;
26772 if (offset + markerLength > dataView.byteLength) {
26773 Roo.log('Invalid meta data: Invalid segment size.');
26777 if(markerBytes == 0xffe1){
26778 _this.parseExifData(
26785 offset += markerLength;
26795 var url = _this.urlAPI.createObjectURL(_this.file);
26797 _this.loadCanvas(url);
26802 reader.readAsArrayBuffer(this.file);
26808 parseExifData : function(dataView, offset, length)
26810 var tiffOffset = offset + 10,
26814 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26815 // No Exif data, might be XMP data instead
26819 // Check for the ASCII code for "Exif" (0x45786966):
26820 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26821 // No Exif data, might be XMP data instead
26824 if (tiffOffset + 8 > dataView.byteLength) {
26825 Roo.log('Invalid Exif data: Invalid segment size.');
26828 // Check for the two null bytes:
26829 if (dataView.getUint16(offset + 8) !== 0x0000) {
26830 Roo.log('Invalid Exif data: Missing byte alignment offset.');
26833 // Check the byte alignment:
26834 switch (dataView.getUint16(tiffOffset)) {
26836 littleEndian = true;
26839 littleEndian = false;
26842 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26845 // Check for the TIFF tag marker (0x002A):
26846 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26847 Roo.log('Invalid Exif data: Missing TIFF marker.');
26850 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26851 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26853 this.parseExifTags(
26856 tiffOffset + dirOffset,
26861 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26866 if (dirOffset + 6 > dataView.byteLength) {
26867 Roo.log('Invalid Exif data: Invalid directory offset.');
26870 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26871 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26872 if (dirEndOffset + 4 > dataView.byteLength) {
26873 Roo.log('Invalid Exif data: Invalid directory size.');
26876 for (i = 0; i < tagsNumber; i += 1) {
26880 dirOffset + 2 + 12 * i, // tag offset
26884 // Return the offset to the next directory:
26885 return dataView.getUint32(dirEndOffset, littleEndian);
26888 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
26890 var tag = dataView.getUint16(offset, littleEndian);
26892 this.exif[tag] = this.getExifValue(
26896 dataView.getUint16(offset + 2, littleEndian), // tag type
26897 dataView.getUint32(offset + 4, littleEndian), // tag length
26902 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26904 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26913 Roo.log('Invalid Exif data: Invalid tag type.');
26917 tagSize = tagType.size * length;
26918 // Determine if the value is contained in the dataOffset bytes,
26919 // or if the value at the dataOffset is a pointer to the actual data:
26920 dataOffset = tagSize > 4 ?
26921 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26922 if (dataOffset + tagSize > dataView.byteLength) {
26923 Roo.log('Invalid Exif data: Invalid data offset.');
26926 if (length === 1) {
26927 return tagType.getValue(dataView, dataOffset, littleEndian);
26930 for (i = 0; i < length; i += 1) {
26931 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26934 if (tagType.ascii) {
26936 // Concatenate the chars:
26937 for (i = 0; i < values.length; i += 1) {
26939 // Ignore the terminating NULL byte(s):
26940 if (c === '\u0000') {
26952 Roo.apply(Roo.bootstrap.UploadCropbox, {
26954 'Orientation': 0x0112
26958 1: 0, //'top-left',
26960 3: 180, //'bottom-right',
26961 // 4: 'bottom-left',
26963 6: 90, //'right-top',
26964 // 7: 'right-bottom',
26965 8: 270 //'left-bottom'
26969 // byte, 8-bit unsigned int:
26971 getValue: function (dataView, dataOffset) {
26972 return dataView.getUint8(dataOffset);
26976 // ascii, 8-bit byte:
26978 getValue: function (dataView, dataOffset) {
26979 return String.fromCharCode(dataView.getUint8(dataOffset));
26984 // short, 16 bit int:
26986 getValue: function (dataView, dataOffset, littleEndian) {
26987 return dataView.getUint16(dataOffset, littleEndian);
26991 // long, 32 bit int:
26993 getValue: function (dataView, dataOffset, littleEndian) {
26994 return dataView.getUint32(dataOffset, littleEndian);
26998 // rational = two long values, first is numerator, second is denominator:
27000 getValue: function (dataView, dataOffset, littleEndian) {
27001 return dataView.getUint32(dataOffset, littleEndian) /
27002 dataView.getUint32(dataOffset + 4, littleEndian);
27006 // slong, 32 bit signed int:
27008 getValue: function (dataView, dataOffset, littleEndian) {
27009 return dataView.getInt32(dataOffset, littleEndian);
27013 // srational, two slongs, first is numerator, second is denominator:
27015 getValue: function (dataView, dataOffset, littleEndian) {
27016 return dataView.getInt32(dataOffset, littleEndian) /
27017 dataView.getInt32(dataOffset + 4, littleEndian);
27027 cls : 'btn-group roo-upload-cropbox-rotate-left',
27028 action : 'rotate-left',
27032 cls : 'btn btn-default',
27033 html : '<i class="fa fa-undo"></i>'
27039 cls : 'btn-group roo-upload-cropbox-picture',
27040 action : 'picture',
27044 cls : 'btn btn-default',
27045 html : '<i class="fa fa-picture-o"></i>'
27051 cls : 'btn-group roo-upload-cropbox-rotate-right',
27052 action : 'rotate-right',
27056 cls : 'btn btn-default',
27057 html : '<i class="fa fa-repeat"></i>'
27065 cls : 'btn-group roo-upload-cropbox-rotate-left',
27066 action : 'rotate-left',
27070 cls : 'btn btn-default',
27071 html : '<i class="fa fa-undo"></i>'
27077 cls : 'btn-group roo-upload-cropbox-download',
27078 action : 'download',
27082 cls : 'btn btn-default',
27083 html : '<i class="fa fa-download"></i>'
27089 cls : 'btn-group roo-upload-cropbox-crop',
27094 cls : 'btn btn-default',
27095 html : '<i class="fa fa-crop"></i>'
27101 cls : 'btn-group roo-upload-cropbox-trash',
27106 cls : 'btn btn-default',
27107 html : '<i class="fa fa-trash"></i>'
27113 cls : 'btn-group roo-upload-cropbox-rotate-right',
27114 action : 'rotate-right',
27118 cls : 'btn btn-default',
27119 html : '<i class="fa fa-repeat"></i>'
27127 cls : 'btn-group roo-upload-cropbox-rotate-left',
27128 action : 'rotate-left',
27132 cls : 'btn btn-default',
27133 html : '<i class="fa fa-undo"></i>'
27139 cls : 'btn-group roo-upload-cropbox-rotate-right',
27140 action : 'rotate-right',
27144 cls : 'btn btn-default',
27145 html : '<i class="fa fa-repeat"></i>'
27158 * @class Roo.bootstrap.DocumentManager
27159 * @extends Roo.bootstrap.Component
27160 * Bootstrap DocumentManager class
27161 * @cfg {String} paramName default 'imageUpload'
27162 * @cfg {String} method default POST
27163 * @cfg {String} url action url
27164 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27165 * @cfg {Boolean} multiple multiple upload default true
27166 * @cfg {Number} thumbSize default 300
27167 * @cfg {String} fieldLabel
27168 * @cfg {Number} labelWidth default 4
27169 * @cfg {String} labelAlign (left|top) default left
27170 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27173 * Create a new DocumentManager
27174 * @param {Object} config The config object
27177 Roo.bootstrap.DocumentManager = function(config){
27178 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27183 * Fire when initial the DocumentManager
27184 * @param {Roo.bootstrap.DocumentManager} this
27189 * inspect selected file
27190 * @param {Roo.bootstrap.DocumentManager} this
27191 * @param {File} file
27196 * Fire when xhr load exception
27197 * @param {Roo.bootstrap.DocumentManager} this
27198 * @param {XMLHttpRequest} xhr
27200 "exception" : true,
27203 * prepare the form data
27204 * @param {Roo.bootstrap.DocumentManager} this
27205 * @param {Object} formData
27210 * Fire when remove the file
27211 * @param {Roo.bootstrap.DocumentManager} this
27212 * @param {Object} file
27217 * Fire after refresh the file
27218 * @param {Roo.bootstrap.DocumentManager} this
27223 * Fire after click the image
27224 * @param {Roo.bootstrap.DocumentManager} this
27225 * @param {Object} file
27230 * Fire when upload a image and editable set to true
27231 * @param {Roo.bootstrap.DocumentManager} this
27232 * @param {Object} file
27236 * @event beforeselectfile
27237 * Fire before select file
27238 * @param {Roo.bootstrap.DocumentManager} this
27240 "beforeselectfile" : true,
27243 * Fire before process file
27244 * @param {Roo.bootstrap.DocumentManager} this
27245 * @param {Object} file
27252 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
27261 paramName : 'imageUpload',
27264 labelAlign : 'left',
27271 getAutoCreate : function()
27273 var managerWidget = {
27275 cls : 'roo-document-manager',
27279 cls : 'roo-document-manager-selector',
27284 cls : 'roo-document-manager-uploader',
27288 cls : 'roo-document-manager-upload-btn',
27289 html : '<i class="fa fa-plus"></i>'
27300 cls : 'column col-md-12',
27305 if(this.fieldLabel.length){
27310 cls : 'column col-md-12',
27311 html : this.fieldLabel
27315 cls : 'column col-md-12',
27320 if(this.labelAlign == 'left'){
27324 cls : 'column col-md-' + this.labelWidth,
27325 html : this.fieldLabel
27329 cls : 'column col-md-' + (12 - this.labelWidth),
27339 cls : 'row clearfix',
27347 initEvents : function()
27349 this.managerEl = this.el.select('.roo-document-manager', true).first();
27350 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27352 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27353 this.selectorEl.hide();
27356 this.selectorEl.attr('multiple', 'multiple');
27359 this.selectorEl.on('change', this.onFileSelected, this);
27361 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27362 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27364 this.uploader.on('click', this.onUploaderClick, this);
27366 this.renderProgressDialog();
27370 window.addEventListener("resize", function() { _this.refresh(); } );
27372 this.fireEvent('initial', this);
27375 renderProgressDialog : function()
27379 this.progressDialog = new Roo.bootstrap.Modal({
27380 cls : 'roo-document-manager-progress-dialog',
27381 allow_close : false,
27391 btnclick : function() {
27392 _this.uploadCancel();
27398 this.progressDialog.render(Roo.get(document.body));
27400 this.progress = new Roo.bootstrap.Progress({
27401 cls : 'roo-document-manager-progress',
27406 this.progress.render(this.progressDialog.getChildContainer());
27408 this.progressBar = new Roo.bootstrap.ProgressBar({
27409 cls : 'roo-document-manager-progress-bar',
27412 aria_valuemax : 12,
27416 this.progressBar.render(this.progress.getChildContainer());
27419 onUploaderClick : function(e)
27421 e.preventDefault();
27423 if(this.fireEvent('beforeselectfile', this) != false){
27424 this.selectorEl.dom.click();
27429 onFileSelected : function(e)
27431 e.preventDefault();
27433 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27437 Roo.each(this.selectorEl.dom.files, function(file){
27438 if(this.fireEvent('inspect', this, file) != false){
27439 this.files.push(file);
27449 this.selectorEl.dom.value = '';
27451 if(!this.files.length){
27455 if(this.boxes > 0 && this.files.length > this.boxes){
27456 this.files = this.files.slice(0, this.boxes);
27459 this.uploader.show();
27461 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27462 this.uploader.hide();
27471 Roo.each(this.files, function(file){
27473 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27474 var f = this.renderPreview(file);
27479 if(file.type.indexOf('image') != -1){
27480 this.delegates.push(
27482 _this.process(file);
27483 }).createDelegate(this)
27491 _this.process(file);
27492 }).createDelegate(this)
27497 this.files = files;
27499 this.delegates = this.delegates.concat(docs);
27501 if(!this.delegates.length){
27506 this.progressBar.aria_valuemax = this.delegates.length;
27513 arrange : function()
27515 if(!this.delegates.length){
27516 this.progressDialog.hide();
27521 var delegate = this.delegates.shift();
27523 this.progressDialog.show();
27525 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27527 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27532 refresh : function()
27534 this.uploader.show();
27536 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27537 this.uploader.hide();
27540 Roo.isTouch ? this.closable(false) : this.closable(true);
27542 this.fireEvent('refresh', this);
27545 onRemove : function(e, el, o)
27547 e.preventDefault();
27549 this.fireEvent('remove', this, o);
27553 remove : function(o)
27557 Roo.each(this.files, function(file){
27558 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27567 this.files = files;
27574 Roo.each(this.files, function(file){
27579 file.target.remove();
27588 onClick : function(e, el, o)
27590 e.preventDefault();
27592 this.fireEvent('click', this, o);
27596 closable : function(closable)
27598 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27600 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27612 xhrOnLoad : function(xhr)
27614 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27618 if (xhr.readyState !== 4) {
27620 this.fireEvent('exception', this, xhr);
27624 var response = Roo.decode(xhr.responseText);
27626 if(!response.success){
27628 this.fireEvent('exception', this, xhr);
27632 var file = this.renderPreview(response.data);
27634 this.files.push(file);
27640 xhrOnError : function(xhr)
27642 Roo.log('xhr on error');
27644 var response = Roo.decode(xhr.responseText);
27651 process : function(file)
27653 if(this.fireEvent('process', this, file) !== false){
27654 if(this.editable && file.type.indexOf('image') != -1){
27655 this.fireEvent('edit', this, file);
27659 this.uploadStart(file, false);
27666 uploadStart : function(file, crop)
27668 this.xhr = new XMLHttpRequest();
27670 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27675 file.xhr = this.xhr;
27677 this.managerEl.createChild({
27679 cls : 'roo-document-manager-loading',
27683 tooltip : file.name,
27684 cls : 'roo-document-manager-thumb',
27685 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27691 this.xhr.open(this.method, this.url, true);
27694 "Accept": "application/json",
27695 "Cache-Control": "no-cache",
27696 "X-Requested-With": "XMLHttpRequest"
27699 for (var headerName in headers) {
27700 var headerValue = headers[headerName];
27702 this.xhr.setRequestHeader(headerName, headerValue);
27708 this.xhr.onload = function()
27710 _this.xhrOnLoad(_this.xhr);
27713 this.xhr.onerror = function()
27715 _this.xhrOnError(_this.xhr);
27718 var formData = new FormData();
27720 formData.append('returnHTML', 'NO');
27723 formData.append('crop', crop);
27726 formData.append(this.paramName, file, file.name);
27728 if(this.fireEvent('prepare', this, formData) != false){
27729 this.xhr.send(formData);
27733 uploadCancel : function()
27740 this.delegates = [];
27742 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27749 renderPreview : function(file)
27751 if(typeof(file.target) != 'undefined' && file.target){
27755 var previewEl = this.managerEl.createChild({
27757 cls : 'roo-document-manager-preview',
27761 tooltip : file.filename,
27762 cls : 'roo-document-manager-thumb',
27763 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27768 html : '<i class="fa fa-times-circle"></i>'
27773 var close = previewEl.select('button.close', true).first();
27775 close.on('click', this.onRemove, this, file);
27777 file.target = previewEl;
27779 var image = previewEl.select('img', true).first();
27783 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27785 image.on('click', this.onClick, this, file);
27791 onPreviewLoad : function(file, image)
27793 if(typeof(file.target) == 'undefined' || !file.target){
27797 var width = image.dom.naturalWidth || image.dom.width;
27798 var height = image.dom.naturalHeight || image.dom.height;
27800 if(width > height){
27801 file.target.addClass('wide');
27805 file.target.addClass('tall');
27810 uploadFromSource : function(file, crop)
27812 this.xhr = new XMLHttpRequest();
27814 this.managerEl.createChild({
27816 cls : 'roo-document-manager-loading',
27820 tooltip : file.name,
27821 cls : 'roo-document-manager-thumb',
27822 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27828 this.xhr.open(this.method, this.url, true);
27831 "Accept": "application/json",
27832 "Cache-Control": "no-cache",
27833 "X-Requested-With": "XMLHttpRequest"
27836 for (var headerName in headers) {
27837 var headerValue = headers[headerName];
27839 this.xhr.setRequestHeader(headerName, headerValue);
27845 this.xhr.onload = function()
27847 _this.xhrOnLoad(_this.xhr);
27850 this.xhr.onerror = function()
27852 _this.xhrOnError(_this.xhr);
27855 var formData = new FormData();
27857 formData.append('returnHTML', 'NO');
27859 formData.append('crop', crop);
27861 if(typeof(file.filename) != 'undefined'){
27862 formData.append('filename', file.filename);
27865 if(typeof(file.mimetype) != 'undefined'){
27866 formData.append('mimetype', file.mimetype);
27869 if(this.fireEvent('prepare', this, formData) != false){
27870 this.xhr.send(formData);
27880 * @class Roo.bootstrap.DocumentViewer
27881 * @extends Roo.bootstrap.Component
27882 * Bootstrap DocumentViewer class
27885 * Create a new DocumentViewer
27886 * @param {Object} config The config object
27889 Roo.bootstrap.DocumentViewer = function(config){
27890 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27895 * Fire after initEvent
27896 * @param {Roo.bootstrap.DocumentViewer} this
27902 * @param {Roo.bootstrap.DocumentViewer} this
27907 * Fire after trash button
27908 * @param {Roo.bootstrap.DocumentViewer} this
27915 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27917 getAutoCreate : function()
27921 cls : 'roo-document-viewer',
27925 cls : 'roo-document-viewer-body',
27929 cls : 'roo-document-viewer-thumb',
27933 cls : 'roo-document-viewer-image'
27941 cls : 'roo-document-viewer-footer',
27944 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27952 cls : 'btn btn-default roo-document-viewer-trash',
27953 html : '<i class="fa fa-trash"></i>'
27966 initEvents : function()
27969 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27970 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27972 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27973 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27975 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27976 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27978 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27979 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27981 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27982 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27984 this.bodyEl.on('click', this.onClick, this);
27986 this.trashBtn.on('click', this.onTrash, this);
27990 initial : function()
27992 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27995 this.fireEvent('initial', this);
27999 onClick : function(e)
28001 e.preventDefault();
28003 this.fireEvent('click', this);
28006 onTrash : function(e)
28008 e.preventDefault();
28010 this.fireEvent('trash', this);
28022 * @class Roo.bootstrap.NavProgressBar
28023 * @extends Roo.bootstrap.Component
28024 * Bootstrap NavProgressBar class
28027 * Create a new nav progress bar
28028 * @param {Object} config The config object
28031 Roo.bootstrap.NavProgressBar = function(config){
28032 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28034 this.bullets = this.bullets || [];
28036 // Roo.bootstrap.NavProgressBar.register(this);
28040 * Fires when the active item changes
28041 * @param {Roo.bootstrap.NavProgressBar} this
28042 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28043 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
28050 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
28055 getAutoCreate : function()
28057 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28061 cls : 'roo-navigation-bar-group',
28065 cls : 'roo-navigation-top-bar'
28069 cls : 'roo-navigation-bullets-bar',
28073 cls : 'roo-navigation-bar'
28080 cls : 'roo-navigation-bottom-bar'
28090 initEvents: function()
28095 onRender : function(ct, position)
28097 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28099 if(this.bullets.length){
28100 Roo.each(this.bullets, function(b){
28109 addItem : function(cfg)
28111 var item = new Roo.bootstrap.NavProgressItem(cfg);
28113 item.parentId = this.id;
28114 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28117 var top = new Roo.bootstrap.Element({
28119 cls : 'roo-navigation-bar-text'
28122 var bottom = new Roo.bootstrap.Element({
28124 cls : 'roo-navigation-bar-text'
28127 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28128 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28130 var topText = new Roo.bootstrap.Element({
28132 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28135 var bottomText = new Roo.bootstrap.Element({
28137 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28140 topText.onRender(top.el, null);
28141 bottomText.onRender(bottom.el, null);
28144 item.bottomEl = bottom;
28147 this.barItems.push(item);
28152 getActive : function()
28154 var active = false;
28156 Roo.each(this.barItems, function(v){
28158 if (!v.isActive()) {
28170 setActiveItem : function(item)
28174 Roo.each(this.barItems, function(v){
28175 if (v.rid == item.rid) {
28179 if (v.isActive()) {
28180 v.setActive(false);
28185 item.setActive(true);
28187 this.fireEvent('changed', this, item, prev);
28190 getBarItem: function(rid)
28194 Roo.each(this.barItems, function(e) {
28195 if (e.rid != rid) {
28206 indexOfItem : function(item)
28210 Roo.each(this.barItems, function(v, i){
28212 if (v.rid != item.rid) {
28223 setActiveNext : function()
28225 var i = this.indexOfItem(this.getActive());
28227 if (i > this.barItems.length) {
28231 this.setActiveItem(this.barItems[i+1]);
28234 setActivePrev : function()
28236 var i = this.indexOfItem(this.getActive());
28242 this.setActiveItem(this.barItems[i-1]);
28245 format : function()
28247 if(!this.barItems.length){
28251 var width = 100 / this.barItems.length;
28253 Roo.each(this.barItems, function(i){
28254 i.el.setStyle('width', width + '%');
28255 i.topEl.el.setStyle('width', width + '%');
28256 i.bottomEl.el.setStyle('width', width + '%');
28265 * Nav Progress Item
28270 * @class Roo.bootstrap.NavProgressItem
28271 * @extends Roo.bootstrap.Component
28272 * Bootstrap NavProgressItem class
28273 * @cfg {String} rid the reference id
28274 * @cfg {Boolean} active (true|false) Is item active default false
28275 * @cfg {Boolean} disabled (true|false) Is item active default false
28276 * @cfg {String} html
28277 * @cfg {String} position (top|bottom) text position default bottom
28278 * @cfg {String} icon show icon instead of number
28281 * Create a new NavProgressItem
28282 * @param {Object} config The config object
28284 Roo.bootstrap.NavProgressItem = function(config){
28285 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28290 * The raw click event for the entire grid.
28291 * @param {Roo.bootstrap.NavProgressItem} this
28292 * @param {Roo.EventObject} e
28299 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
28305 position : 'bottom',
28308 getAutoCreate : function()
28310 var iconCls = 'roo-navigation-bar-item-icon';
28312 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28316 cls: 'roo-navigation-bar-item',
28326 cfg.cls += ' active';
28329 cfg.cls += ' disabled';
28335 disable : function()
28337 this.setDisabled(true);
28340 enable : function()
28342 this.setDisabled(false);
28345 initEvents: function()
28347 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28349 this.iconEl.on('click', this.onClick, this);
28352 onClick : function(e)
28354 e.preventDefault();
28360 if(this.fireEvent('click', this, e) === false){
28364 this.parent().setActiveItem(this);
28367 isActive: function ()
28369 return this.active;
28372 setActive : function(state)
28374 if(this.active == state){
28378 this.active = state;
28381 this.el.addClass('active');
28385 this.el.removeClass('active');
28390 setDisabled : function(state)
28392 if(this.disabled == state){
28396 this.disabled = state;
28399 this.el.addClass('disabled');
28403 this.el.removeClass('disabled');
28406 tooltipEl : function()
28408 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28421 * @class Roo.bootstrap.FieldLabel
28422 * @extends Roo.bootstrap.Component
28423 * Bootstrap FieldLabel class
28424 * @cfg {String} html contents of the element
28425 * @cfg {String} tag tag of the element default label
28426 * @cfg {String} cls class of the element
28427 * @cfg {String} target label target
28428 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28429 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28430 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28431 * @cfg {String} iconTooltip default "This field is required"
28434 * Create a new FieldLabel
28435 * @param {Object} config The config object
28438 Roo.bootstrap.FieldLabel = function(config){
28439 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28444 * Fires after the field has been marked as invalid.
28445 * @param {Roo.form.FieldLabel} this
28446 * @param {String} msg The validation message
28451 * Fires after the field has been validated with no errors.
28452 * @param {Roo.form.FieldLabel} this
28458 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28465 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28466 validClass : 'text-success fa fa-lg fa-check',
28467 iconTooltip : 'This field is required',
28469 getAutoCreate : function(){
28473 cls : 'roo-bootstrap-field-label ' + this.cls,
28479 tooltip : this.iconTooltip
28491 initEvents: function()
28493 Roo.bootstrap.Element.superclass.initEvents.call(this);
28495 this.iconEl = this.el.select('i', true).first();
28497 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28499 Roo.bootstrap.FieldLabel.register(this);
28503 * Mark this field as valid
28505 markValid : function()
28507 this.iconEl.show();
28509 this.iconEl.removeClass(this.invalidClass);
28511 this.iconEl.addClass(this.validClass);
28513 this.fireEvent('valid', this);
28517 * Mark this field as invalid
28518 * @param {String} msg The validation message
28520 markInvalid : function(msg)
28522 this.iconEl.show();
28524 this.iconEl.removeClass(this.validClass);
28526 this.iconEl.addClass(this.invalidClass);
28528 this.fireEvent('invalid', this, msg);
28534 Roo.apply(Roo.bootstrap.FieldLabel, {
28539 * register a FieldLabel Group
28540 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28542 register : function(label)
28544 if(this.groups.hasOwnProperty(label.target)){
28548 this.groups[label.target] = label;
28552 * fetch a FieldLabel Group based on the target
28553 * @param {string} target
28554 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28556 get: function(target) {
28557 if (typeof(this.groups[target]) == 'undefined') {
28561 return this.groups[target] ;
28570 * page DateSplitField.
28576 * @class Roo.bootstrap.DateSplitField
28577 * @extends Roo.bootstrap.Component
28578 * Bootstrap DateSplitField class
28579 * @cfg {string} fieldLabel - the label associated
28580 * @cfg {Number} labelWidth set the width of label (0-12)
28581 * @cfg {String} labelAlign (top|left)
28582 * @cfg {Boolean} dayAllowBlank (true|false) default false
28583 * @cfg {Boolean} monthAllowBlank (true|false) default false
28584 * @cfg {Boolean} yearAllowBlank (true|false) default false
28585 * @cfg {string} dayPlaceholder
28586 * @cfg {string} monthPlaceholder
28587 * @cfg {string} yearPlaceholder
28588 * @cfg {string} dayFormat default 'd'
28589 * @cfg {string} monthFormat default 'm'
28590 * @cfg {string} yearFormat default 'Y'
28594 * Create a new DateSplitField
28595 * @param {Object} config The config object
28598 Roo.bootstrap.DateSplitField = function(config){
28599 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28605 * getting the data of years
28606 * @param {Roo.bootstrap.DateSplitField} this
28607 * @param {Object} years
28612 * getting the data of days
28613 * @param {Roo.bootstrap.DateSplitField} this
28614 * @param {Object} days
28619 * Fires after the field has been marked as invalid.
28620 * @param {Roo.form.Field} this
28621 * @param {String} msg The validation message
28626 * Fires after the field has been validated with no errors.
28627 * @param {Roo.form.Field} this
28633 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28636 labelAlign : 'top',
28638 dayAllowBlank : false,
28639 monthAllowBlank : false,
28640 yearAllowBlank : false,
28641 dayPlaceholder : '',
28642 monthPlaceholder : '',
28643 yearPlaceholder : '',
28647 isFormField : true,
28649 getAutoCreate : function()
28653 cls : 'row roo-date-split-field-group',
28658 cls : 'form-hidden-field roo-date-split-field-group-value',
28664 if(this.fieldLabel){
28667 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28671 html : this.fieldLabel
28677 Roo.each(['day', 'month', 'year'], function(t){
28680 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28687 inputEl: function ()
28689 return this.el.select('.roo-date-split-field-group-value', true).first();
28692 onRender : function(ct, position)
28696 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28698 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28700 this.dayField = new Roo.bootstrap.ComboBox({
28701 allowBlank : this.dayAllowBlank,
28702 alwaysQuery : true,
28703 displayField : 'value',
28706 forceSelection : true,
28708 placeholder : this.dayPlaceholder,
28709 selectOnFocus : true,
28710 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28711 triggerAction : 'all',
28713 valueField : 'value',
28714 store : new Roo.data.SimpleStore({
28715 data : (function() {
28717 _this.fireEvent('days', _this, days);
28720 fields : [ 'value' ]
28723 select : function (_self, record, index)
28725 _this.setValue(_this.getValue());
28730 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28732 this.monthField = new Roo.bootstrap.MonthField({
28733 after : '<i class=\"fa fa-calendar\"></i>',
28734 allowBlank : this.monthAllowBlank,
28735 placeholder : this.monthPlaceholder,
28738 render : function (_self)
28740 this.el.select('span.input-group-addon', true).first().on('click', function(e){
28741 e.preventDefault();
28745 select : function (_self, oldvalue, newvalue)
28747 _this.setValue(_this.getValue());
28752 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28754 this.yearField = new Roo.bootstrap.ComboBox({
28755 allowBlank : this.yearAllowBlank,
28756 alwaysQuery : true,
28757 displayField : 'value',
28760 forceSelection : true,
28762 placeholder : this.yearPlaceholder,
28763 selectOnFocus : true,
28764 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28765 triggerAction : 'all',
28767 valueField : 'value',
28768 store : new Roo.data.SimpleStore({
28769 data : (function() {
28771 _this.fireEvent('years', _this, years);
28774 fields : [ 'value' ]
28777 select : function (_self, record, index)
28779 _this.setValue(_this.getValue());
28784 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28787 setValue : function(v, format)
28789 this.inputEl.dom.value = v;
28791 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28793 var d = Date.parseDate(v, f);
28800 this.setDay(d.format(this.dayFormat));
28801 this.setMonth(d.format(this.monthFormat));
28802 this.setYear(d.format(this.yearFormat));
28809 setDay : function(v)
28811 this.dayField.setValue(v);
28812 this.inputEl.dom.value = this.getValue();
28817 setMonth : function(v)
28819 this.monthField.setValue(v, true);
28820 this.inputEl.dom.value = this.getValue();
28825 setYear : function(v)
28827 this.yearField.setValue(v);
28828 this.inputEl.dom.value = this.getValue();
28833 getDay : function()
28835 return this.dayField.getValue();
28838 getMonth : function()
28840 return this.monthField.getValue();
28843 getYear : function()
28845 return this.yearField.getValue();
28848 getValue : function()
28850 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28852 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28862 this.inputEl.dom.value = '';
28867 validate : function()
28869 var d = this.dayField.validate();
28870 var m = this.monthField.validate();
28871 var y = this.yearField.validate();
28876 (!this.dayAllowBlank && !d) ||
28877 (!this.monthAllowBlank && !m) ||
28878 (!this.yearAllowBlank && !y)
28883 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28892 this.markInvalid();
28897 markValid : function()
28900 var label = this.el.select('label', true).first();
28901 var icon = this.el.select('i.fa-star', true).first();
28907 this.fireEvent('valid', this);
28911 * Mark this field as invalid
28912 * @param {String} msg The validation message
28914 markInvalid : function(msg)
28917 var label = this.el.select('label', true).first();
28918 var icon = this.el.select('i.fa-star', true).first();
28920 if(label && !icon){
28921 this.el.select('.roo-date-split-field-label', true).createChild({
28923 cls : 'text-danger fa fa-lg fa-star',
28924 tooltip : 'This field is required',
28925 style : 'margin-right:5px;'
28929 this.fireEvent('invalid', this, msg);
28932 clearInvalid : function()
28934 var label = this.el.select('label', true).first();
28935 var icon = this.el.select('i.fa-star', true).first();
28941 this.fireEvent('valid', this);
28944 getName: function()
28954 * http://masonry.desandro.com
28956 * The idea is to render all the bricks based on vertical width...
28958 * The original code extends 'outlayer' - we might need to use that....
28964 * @class Roo.bootstrap.LayoutMasonry
28965 * @extends Roo.bootstrap.Component
28966 * Bootstrap Layout Masonry class
28969 * Create a new Element
28970 * @param {Object} config The config object
28973 Roo.bootstrap.LayoutMasonry = function(config){
28974 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28980 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
28983 * @cfg {Boolean} isLayoutInstant = no animation?
28985 isLayoutInstant : false, // needed?
28988 * @cfg {Number} boxWidth width of the columns
28993 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
28998 * @cfg {Number} padWidth padding below box..
29003 * @cfg {Number} gutter gutter width..
29008 * @cfg {Number} maxCols maximum number of columns
29014 * @cfg {Boolean} isAutoInitial defalut true
29016 isAutoInitial : true,
29021 * @cfg {Boolean} isHorizontal defalut false
29023 isHorizontal : false,
29025 currentSize : null,
29031 bricks: null, //CompositeElement
29035 _isLayoutInited : false,
29037 // isAlternative : false, // only use for vertical layout...
29040 * @cfg {Number} alternativePadWidth padding below box..
29042 alternativePadWidth : 50,
29044 getAutoCreate : function(){
29048 cls: 'blog-masonary-wrapper ' + this.cls,
29050 cls : 'mas-boxes masonary'
29057 getChildContainer: function( )
29059 if (this.boxesEl) {
29060 return this.boxesEl;
29063 this.boxesEl = this.el.select('.mas-boxes').first();
29065 return this.boxesEl;
29069 initEvents : function()
29073 if(this.isAutoInitial){
29074 Roo.log('hook children rendered');
29075 this.on('childrenrendered', function() {
29076 Roo.log('children rendered');
29082 initial : function()
29084 this.currentSize = this.el.getBox(true);
29086 Roo.EventManager.onWindowResize(this.resize, this);
29088 if(!this.isAutoInitial){
29096 //this.layout.defer(500,this);
29100 resize : function()
29104 var cs = this.el.getBox(true);
29106 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29107 Roo.log("no change in with or X");
29111 this.currentSize = cs;
29117 layout : function()
29119 this._resetLayout();
29121 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29123 this.layoutItems( isInstant );
29125 this._isLayoutInited = true;
29129 _resetLayout : function()
29131 if(this.isHorizontal){
29132 this.horizontalMeasureColumns();
29136 this.verticalMeasureColumns();
29140 verticalMeasureColumns : function()
29142 this.getContainerWidth();
29144 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29145 // this.colWidth = Math.floor(this.containerWidth * 0.8);
29149 var boxWidth = this.boxWidth + this.padWidth;
29151 if(this.containerWidth < this.boxWidth){
29152 boxWidth = this.containerWidth
29155 var containerWidth = this.containerWidth;
29157 var cols = Math.floor(containerWidth / boxWidth);
29159 this.cols = Math.max( cols, 1 );
29161 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29163 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29165 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29167 this.colWidth = boxWidth + avail - this.padWidth;
29169 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29170 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
29173 horizontalMeasureColumns : function()
29175 this.getContainerWidth();
29177 var boxWidth = this.boxWidth;
29179 if(this.containerWidth < boxWidth){
29180 boxWidth = this.containerWidth;
29183 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29185 this.el.setHeight(boxWidth);
29189 getContainerWidth : function()
29191 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
29194 layoutItems : function( isInstant )
29196 var items = Roo.apply([], this.bricks);
29198 if(this.isHorizontal){
29199 this._horizontalLayoutItems( items , isInstant );
29203 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29204 // this._verticalAlternativeLayoutItems( items , isInstant );
29208 this._verticalLayoutItems( items , isInstant );
29212 _verticalLayoutItems : function ( items , isInstant)
29214 if ( !items || !items.length ) {
29219 ['xs', 'xs', 'xs', 'tall'],
29220 ['xs', 'xs', 'tall'],
29221 ['xs', 'xs', 'sm'],
29222 ['xs', 'xs', 'xs'],
29228 ['sm', 'xs', 'xs'],
29232 ['tall', 'xs', 'xs', 'xs'],
29233 ['tall', 'xs', 'xs'],
29245 Roo.each(items, function(item, k){
29247 switch (item.size) {
29248 // these layouts take up a full box,
29259 boxes.push([item]);
29282 var filterPattern = function(box, length)
29290 var pattern = box.slice(0, length);
29294 Roo.each(pattern, function(i){
29295 format.push(i.size);
29298 Roo.each(standard, function(s){
29300 if(String(s) != String(format)){
29309 if(!match && length == 1){
29314 filterPattern(box, length - 1);
29318 queue.push(pattern);
29320 box = box.slice(length, box.length);
29322 filterPattern(box, 4);
29328 Roo.each(boxes, function(box, k){
29334 if(box.length == 1){
29339 filterPattern(box, 4);
29343 this._processVerticalLayoutQueue( queue, isInstant );
29347 // _verticalAlternativeLayoutItems : function( items , isInstant )
29349 // if ( !items || !items.length ) {
29353 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
29357 _horizontalLayoutItems : function ( items , isInstant)
29359 if ( !items || !items.length || items.length < 3) {
29365 var eItems = items.slice(0, 3);
29367 items = items.slice(3, items.length);
29370 ['xs', 'xs', 'xs', 'wide'],
29371 ['xs', 'xs', 'wide'],
29372 ['xs', 'xs', 'sm'],
29373 ['xs', 'xs', 'xs'],
29379 ['sm', 'xs', 'xs'],
29383 ['wide', 'xs', 'xs', 'xs'],
29384 ['wide', 'xs', 'xs'],
29397 Roo.each(items, function(item, k){
29399 switch (item.size) {
29410 boxes.push([item]);
29434 var filterPattern = function(box, length)
29442 var pattern = box.slice(0, length);
29446 Roo.each(pattern, function(i){
29447 format.push(i.size);
29450 Roo.each(standard, function(s){
29452 if(String(s) != String(format)){
29461 if(!match && length == 1){
29466 filterPattern(box, length - 1);
29470 queue.push(pattern);
29472 box = box.slice(length, box.length);
29474 filterPattern(box, 4);
29480 Roo.each(boxes, function(box, k){
29486 if(box.length == 1){
29491 filterPattern(box, 4);
29498 var pos = this.el.getBox(true);
29502 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29504 var hit_end = false;
29506 Roo.each(queue, function(box){
29510 Roo.each(box, function(b){
29512 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29522 Roo.each(box, function(b){
29524 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29527 mx = Math.max(mx, b.x);
29531 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29535 Roo.each(box, function(b){
29537 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29551 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29554 /** Sets position of item in DOM
29555 * @param {Element} item
29556 * @param {Number} x - horizontal position
29557 * @param {Number} y - vertical position
29558 * @param {Boolean} isInstant - disables transitions
29560 _processVerticalLayoutQueue : function( queue, isInstant )
29562 var pos = this.el.getBox(true);
29567 for (var i = 0; i < this.cols; i++){
29571 Roo.each(queue, function(box, k){
29573 var col = k % this.cols;
29575 Roo.each(box, function(b,kk){
29577 b.el.position('absolute');
29579 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29580 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29582 if(b.size == 'md-left' || b.size == 'md-right'){
29583 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29584 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29587 b.el.setWidth(width);
29588 b.el.setHeight(height);
29590 b.el.select('iframe',true).setSize(width,height);
29594 for (var i = 0; i < this.cols; i++){
29596 if(maxY[i] < maxY[col]){
29601 col = Math.min(col, i);
29605 x = pos.x + col * (this.colWidth + this.padWidth);
29609 var positions = [];
29611 switch (box.length){
29613 positions = this.getVerticalOneBoxColPositions(x, y, box);
29616 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29619 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29622 positions = this.getVerticalFourBoxColPositions(x, y, box);
29628 Roo.each(box, function(b,kk){
29630 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29632 var sz = b.el.getSize();
29634 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29642 for (var i = 0; i < this.cols; i++){
29643 mY = Math.max(mY, maxY[i]);
29646 this.el.setHeight(mY - pos.y);
29650 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29652 // var pos = this.el.getBox(true);
29655 // var maxX = pos.right;
29657 // var maxHeight = 0;
29659 // Roo.each(items, function(item, k){
29663 // item.el.position('absolute');
29665 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29667 // item.el.setWidth(width);
29669 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29671 // item.el.setHeight(height);
29674 // item.el.setXY([x, y], isInstant ? false : true);
29676 // item.el.setXY([maxX - width, y], isInstant ? false : true);
29679 // y = y + height + this.alternativePadWidth;
29681 // maxHeight = maxHeight + height + this.alternativePadWidth;
29685 // this.el.setHeight(maxHeight);
29689 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29691 var pos = this.el.getBox(true);
29696 var maxX = pos.right;
29698 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29700 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29702 Roo.each(queue, function(box, k){
29704 Roo.each(box, function(b, kk){
29706 b.el.position('absolute');
29708 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29709 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29711 if(b.size == 'md-left' || b.size == 'md-right'){
29712 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29713 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29716 b.el.setWidth(width);
29717 b.el.setHeight(height);
29725 var positions = [];
29727 switch (box.length){
29729 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29732 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29735 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29738 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29744 Roo.each(box, function(b,kk){
29746 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29748 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29756 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29758 Roo.each(eItems, function(b,k){
29760 b.size = (k == 0) ? 'sm' : 'xs';
29761 b.x = (k == 0) ? 2 : 1;
29762 b.y = (k == 0) ? 2 : 1;
29764 b.el.position('absolute');
29766 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29768 b.el.setWidth(width);
29770 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29772 b.el.setHeight(height);
29776 var positions = [];
29779 x : maxX - this.unitWidth * 2 - this.gutter,
29784 x : maxX - this.unitWidth,
29785 y : minY + (this.unitWidth + this.gutter) * 2
29789 x : maxX - this.unitWidth * 3 - this.gutter * 2,
29793 Roo.each(eItems, function(b,k){
29795 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29801 getVerticalOneBoxColPositions : function(x, y, box)
29805 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29807 if(box[0].size == 'md-left'){
29811 if(box[0].size == 'md-right'){
29816 x : x + (this.unitWidth + this.gutter) * rand,
29823 getVerticalTwoBoxColPositions : function(x, y, box)
29827 if(box[0].size == 'xs'){
29831 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29835 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29849 x : x + (this.unitWidth + this.gutter) * 2,
29850 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29857 getVerticalThreeBoxColPositions : function(x, y, box)
29861 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29869 x : x + (this.unitWidth + this.gutter) * 1,
29874 x : x + (this.unitWidth + this.gutter) * 2,
29882 if(box[0].size == 'xs' && box[1].size == 'xs'){
29891 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29895 x : x + (this.unitWidth + this.gutter) * 1,
29909 x : x + (this.unitWidth + this.gutter) * 2,
29914 x : x + (this.unitWidth + this.gutter) * 2,
29915 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29922 getVerticalFourBoxColPositions : function(x, y, box)
29926 if(box[0].size == 'xs'){
29935 y : y + (this.unitHeight + this.gutter) * 1
29940 y : y + (this.unitHeight + this.gutter) * 2
29944 x : x + (this.unitWidth + this.gutter) * 1,
29958 x : x + (this.unitWidth + this.gutter) * 2,
29963 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29964 y : y + (this.unitHeight + this.gutter) * 1
29968 x : x + (this.unitWidth + this.gutter) * 2,
29969 y : y + (this.unitWidth + this.gutter) * 2
29976 getHorizontalOneBoxColPositions : function(maxX, minY, box)
29980 if(box[0].size == 'md-left'){
29982 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29989 if(box[0].size == 'md-right'){
29991 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29992 y : minY + (this.unitWidth + this.gutter) * 1
29998 var rand = Math.floor(Math.random() * (4 - box[0].y));
30001 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30002 y : minY + (this.unitWidth + this.gutter) * rand
30009 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30013 if(box[0].size == 'xs'){
30016 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30021 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30022 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30030 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30035 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30036 y : minY + (this.unitWidth + this.gutter) * 2
30043 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30047 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30050 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30055 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30056 y : minY + (this.unitWidth + this.gutter) * 1
30060 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30061 y : minY + (this.unitWidth + this.gutter) * 2
30068 if(box[0].size == 'xs' && box[1].size == 'xs'){
30071 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30076 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30081 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30082 y : minY + (this.unitWidth + this.gutter) * 1
30090 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30095 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30096 y : minY + (this.unitWidth + this.gutter) * 2
30100 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30101 y : minY + (this.unitWidth + this.gutter) * 2
30108 getHorizontalFourBoxColPositions : function(maxX, minY, box)
30112 if(box[0].size == 'xs'){
30115 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30120 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30125 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),
30130 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30131 y : minY + (this.unitWidth + this.gutter) * 1
30139 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30144 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30145 y : minY + (this.unitWidth + this.gutter) * 2
30149 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30150 y : minY + (this.unitWidth + this.gutter) * 2
30154 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),
30155 y : minY + (this.unitWidth + this.gutter) * 2
30169 * http://masonry.desandro.com
30171 * The idea is to render all the bricks based on vertical width...
30173 * The original code extends 'outlayer' - we might need to use that....
30179 * @class Roo.bootstrap.LayoutMasonryAuto
30180 * @extends Roo.bootstrap.Component
30181 * Bootstrap Layout Masonry class
30184 * Create a new Element
30185 * @param {Object} config The config object
30188 Roo.bootstrap.LayoutMasonryAuto = function(config){
30189 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30192 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
30195 * @cfg {Boolean} isFitWidth - resize the width..
30197 isFitWidth : false, // options..
30199 * @cfg {Boolean} isOriginLeft = left align?
30201 isOriginLeft : true,
30203 * @cfg {Boolean} isOriginTop = top align?
30205 isOriginTop : false,
30207 * @cfg {Boolean} isLayoutInstant = no animation?
30209 isLayoutInstant : false, // needed?
30211 * @cfg {Boolean} isResizingContainer = not sure if this is used..
30213 isResizingContainer : true,
30215 * @cfg {Number} columnWidth width of the columns
30221 * @cfg {Number} maxCols maximum number of columns
30226 * @cfg {Number} padHeight padding below box..
30232 * @cfg {Boolean} isAutoInitial defalut true
30235 isAutoInitial : true,
30241 initialColumnWidth : 0,
30242 currentSize : null,
30244 colYs : null, // array.
30251 bricks: null, //CompositeElement
30252 cols : 0, // array?
30253 // element : null, // wrapped now this.el
30254 _isLayoutInited : null,
30257 getAutoCreate : function(){
30261 cls: 'blog-masonary-wrapper ' + this.cls,
30263 cls : 'mas-boxes masonary'
30270 getChildContainer: function( )
30272 if (this.boxesEl) {
30273 return this.boxesEl;
30276 this.boxesEl = this.el.select('.mas-boxes').first();
30278 return this.boxesEl;
30282 initEvents : function()
30286 if(this.isAutoInitial){
30287 Roo.log('hook children rendered');
30288 this.on('childrenrendered', function() {
30289 Roo.log('children rendered');
30296 initial : function()
30298 this.reloadItems();
30300 this.currentSize = this.el.getBox(true);
30302 /// was window resize... - let's see if this works..
30303 Roo.EventManager.onWindowResize(this.resize, this);
30305 if(!this.isAutoInitial){
30310 this.layout.defer(500,this);
30313 reloadItems: function()
30315 this.bricks = this.el.select('.masonry-brick', true);
30317 this.bricks.each(function(b) {
30318 //Roo.log(b.getSize());
30319 if (!b.attr('originalwidth')) {
30320 b.attr('originalwidth', b.getSize().width);
30325 Roo.log(this.bricks.elements.length);
30328 resize : function()
30331 var cs = this.el.getBox(true);
30333 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30334 Roo.log("no change in with or X");
30337 this.currentSize = cs;
30341 layout : function()
30344 this._resetLayout();
30345 //this._manageStamps();
30347 // don't animate first layout
30348 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30349 this.layoutItems( isInstant );
30351 // flag for initalized
30352 this._isLayoutInited = true;
30355 layoutItems : function( isInstant )
30357 //var items = this._getItemsForLayout( this.items );
30358 // original code supports filtering layout items.. we just ignore it..
30360 this._layoutItems( this.bricks , isInstant );
30362 this._postLayout();
30364 _layoutItems : function ( items , isInstant)
30366 //this.fireEvent( 'layout', this, items );
30369 if ( !items || !items.elements.length ) {
30370 // no items, emit event with empty array
30375 items.each(function(item) {
30376 Roo.log("layout item");
30378 // get x/y object from method
30379 var position = this._getItemLayoutPosition( item );
30381 position.item = item;
30382 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30383 queue.push( position );
30386 this._processLayoutQueue( queue );
30388 /** Sets position of item in DOM
30389 * @param {Element} item
30390 * @param {Number} x - horizontal position
30391 * @param {Number} y - vertical position
30392 * @param {Boolean} isInstant - disables transitions
30394 _processLayoutQueue : function( queue )
30396 for ( var i=0, len = queue.length; i < len; i++ ) {
30397 var obj = queue[i];
30398 obj.item.position('absolute');
30399 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30405 * Any logic you want to do after each layout,
30406 * i.e. size the container
30408 _postLayout : function()
30410 this.resizeContainer();
30413 resizeContainer : function()
30415 if ( !this.isResizingContainer ) {
30418 var size = this._getContainerSize();
30420 this.el.setSize(size.width,size.height);
30421 this.boxesEl.setSize(size.width,size.height);
30427 _resetLayout : function()
30429 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30430 this.colWidth = this.el.getWidth();
30431 //this.gutter = this.el.getWidth();
30433 this.measureColumns();
30439 this.colYs.push( 0 );
30445 measureColumns : function()
30447 this.getContainerWidth();
30448 // if columnWidth is 0, default to outerWidth of first item
30449 if ( !this.columnWidth ) {
30450 var firstItem = this.bricks.first();
30451 Roo.log(firstItem);
30452 this.columnWidth = this.containerWidth;
30453 if (firstItem && firstItem.attr('originalwidth') ) {
30454 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30456 // columnWidth fall back to item of first element
30457 Roo.log("set column width?");
30458 this.initialColumnWidth = this.columnWidth ;
30460 // if first elem has no width, default to size of container
30465 if (this.initialColumnWidth) {
30466 this.columnWidth = this.initialColumnWidth;
30471 // column width is fixed at the top - however if container width get's smaller we should
30474 // this bit calcs how man columns..
30476 var columnWidth = this.columnWidth += this.gutter;
30478 // calculate columns
30479 var containerWidth = this.containerWidth + this.gutter;
30481 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30482 // fix rounding errors, typically with gutters
30483 var excess = columnWidth - containerWidth % columnWidth;
30486 // if overshoot is less than a pixel, round up, otherwise floor it
30487 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30488 cols = Math[ mathMethod ]( cols );
30489 this.cols = Math.max( cols, 1 );
30490 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30492 // padding positioning..
30493 var totalColWidth = this.cols * this.columnWidth;
30494 var padavail = this.containerWidth - totalColWidth;
30495 // so for 2 columns - we need 3 'pads'
30497 var padNeeded = (1+this.cols) * this.padWidth;
30499 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30501 this.columnWidth += padExtra
30502 //this.padWidth = Math.floor(padavail / ( this.cols));
30504 // adjust colum width so that padding is fixed??
30506 // we have 3 columns ... total = width * 3
30507 // we have X left over... that should be used by
30509 //if (this.expandC) {
30517 getContainerWidth : function()
30519 /* // container is parent if fit width
30520 var container = this.isFitWidth ? this.element.parentNode : this.element;
30521 // check that this.size and size are there
30522 // IE8 triggers resize on body size change, so they might not be
30524 var size = getSize( container ); //FIXME
30525 this.containerWidth = size && size.innerWidth; //FIXME
30528 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30532 _getItemLayoutPosition : function( item ) // what is item?
30534 // we resize the item to our columnWidth..
30536 item.setWidth(this.columnWidth);
30537 item.autoBoxAdjust = false;
30539 var sz = item.getSize();
30541 // how many columns does this brick span
30542 var remainder = this.containerWidth % this.columnWidth;
30544 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30545 // round if off by 1 pixel, otherwise use ceil
30546 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30547 colSpan = Math.min( colSpan, this.cols );
30549 // normally this should be '1' as we dont' currently allow multi width columns..
30551 var colGroup = this._getColGroup( colSpan );
30552 // get the minimum Y value from the columns
30553 var minimumY = Math.min.apply( Math, colGroup );
30554 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30556 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30558 // position the brick
30560 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30561 y: this.currentSize.y + minimumY + this.padHeight
30565 // apply setHeight to necessary columns
30566 var setHeight = minimumY + sz.height + this.padHeight;
30567 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30569 var setSpan = this.cols + 1 - colGroup.length;
30570 for ( var i = 0; i < setSpan; i++ ) {
30571 this.colYs[ shortColIndex + i ] = setHeight ;
30578 * @param {Number} colSpan - number of columns the element spans
30579 * @returns {Array} colGroup
30581 _getColGroup : function( colSpan )
30583 if ( colSpan < 2 ) {
30584 // if brick spans only one column, use all the column Ys
30589 // how many different places could this brick fit horizontally
30590 var groupCount = this.cols + 1 - colSpan;
30591 // for each group potential horizontal position
30592 for ( var i = 0; i < groupCount; i++ ) {
30593 // make an array of colY values for that one group
30594 var groupColYs = this.colYs.slice( i, i + colSpan );
30595 // and get the max value of the array
30596 colGroup[i] = Math.max.apply( Math, groupColYs );
30601 _manageStamp : function( stamp )
30603 var stampSize = stamp.getSize();
30604 var offset = stamp.getBox();
30605 // get the columns that this stamp affects
30606 var firstX = this.isOriginLeft ? offset.x : offset.right;
30607 var lastX = firstX + stampSize.width;
30608 var firstCol = Math.floor( firstX / this.columnWidth );
30609 firstCol = Math.max( 0, firstCol );
30611 var lastCol = Math.floor( lastX / this.columnWidth );
30612 // lastCol should not go over if multiple of columnWidth #425
30613 lastCol -= lastX % this.columnWidth ? 0 : 1;
30614 lastCol = Math.min( this.cols - 1, lastCol );
30616 // set colYs to bottom of the stamp
30617 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30620 for ( var i = firstCol; i <= lastCol; i++ ) {
30621 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30626 _getContainerSize : function()
30628 this.maxY = Math.max.apply( Math, this.colYs );
30633 if ( this.isFitWidth ) {
30634 size.width = this._getContainerFitWidth();
30640 _getContainerFitWidth : function()
30642 var unusedCols = 0;
30643 // count unused columns
30646 if ( this.colYs[i] !== 0 ) {
30651 // fit container to columns that have been used
30652 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30655 needsResizeLayout : function()
30657 var previousWidth = this.containerWidth;
30658 this.getContainerWidth();
30659 return previousWidth !== this.containerWidth;
30674 * @class Roo.bootstrap.MasonryBrick
30675 * @extends Roo.bootstrap.Component
30676 * Bootstrap MasonryBrick class
30679 * Create a new MasonryBrick
30680 * @param {Object} config The config object
30683 Roo.bootstrap.MasonryBrick = function(config){
30684 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30690 * When a MasonryBrick is clcik
30691 * @param {Roo.bootstrap.MasonryBrick} this
30692 * @param {Roo.EventObject} e
30698 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
30701 * @cfg {String} title
30705 * @cfg {String} html
30709 * @cfg {String} bgimage
30713 * @cfg {String} videourl
30717 * @cfg {String} cls
30721 * @cfg {String} href
30725 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30730 * @cfg {String} (center|bottom) placetitle
30734 getAutoCreate : function()
30736 var cls = 'masonry-brick';
30738 if(this.href.length){
30739 cls += ' masonry-brick-link';
30742 if(this.bgimage.length){
30743 cls += ' masonry-brick-image';
30747 cls += ' masonry-' + this.size + '-brick';
30750 if(this.placetitle.length){
30752 switch (this.placetitle) {
30754 cls += ' masonry-center-title';
30757 cls += ' masonry-bottom-title';
30764 if(!this.html.length && !this.bgimage.length){
30765 cls += ' masonry-center-title';
30768 if(!this.html.length && this.bgimage.length){
30769 cls += ' masonry-bottom-title';
30774 cls += ' ' + this.cls;
30778 tag: (this.href.length) ? 'a' : 'div',
30783 cls: 'masonry-brick-paragraph',
30789 if(this.href.length){
30790 cfg.href = this.href;
30793 var cn = cfg.cn[0].cn;
30795 if(this.title.length){
30798 cls: 'masonry-brick-title',
30803 if(this.html.length){
30806 cls: 'masonry-brick-text',
30810 if (!this.title.length && !this.html.length) {
30811 cfg.cn[0].cls += ' hide';
30814 if(this.bgimage.length){
30817 cls: 'masonry-brick-image-view',
30821 if(this.videourl.length){
30822 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30823 // youtube support only?
30826 cls: 'masonry-brick-image-view',
30829 allowfullscreen : true
30838 initEvents: function()
30840 switch (this.size) {
30842 // this.intSize = 1;
30847 // this.intSize = 2;
30854 // this.intSize = 3;
30859 // this.intSize = 3;
30864 // this.intSize = 3;
30869 // this.intSize = 3;
30881 this.el.on('touchstart', this.onTouchStart, this);
30882 this.el.on('touchmove', this.onTouchMove, this);
30883 this.el.on('touchend', this.onTouchEnd, this);
30884 this.el.on('contextmenu', this.onContextMenu, this);
30886 this.el.on('mouseenter' ,this.enter, this);
30887 this.el.on('mouseleave', this.leave, this);
30890 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30891 this.parent().bricks.push(this);
30896 onClick: function(e, el)
30902 var time = this.endTimer - this.startTimer;
30910 e.preventDefault();
30913 enter: function(e, el)
30915 e.preventDefault();
30917 if(this.bgimage.length && this.html.length){
30918 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30922 leave: function(e, el)
30924 e.preventDefault();
30926 if(this.bgimage.length && this.html.length){
30927 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30931 onTouchStart: function(e, el)
30933 // e.preventDefault();
30935 this.touchmoved = false;
30937 if(!this.bgimage.length || !this.html.length){
30941 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30943 this.timer = new Date().getTime();
30947 onTouchMove: function(e, el)
30949 this.touchmoved = true;
30952 onContextMenu : function(e,el)
30954 e.preventDefault();
30955 e.stopPropagation();
30959 onTouchEnd: function(e, el)
30961 // e.preventDefault();
30963 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30970 if(!this.bgimage.length || !this.html.length){
30972 if(this.href.length){
30973 window.location.href = this.href;
30979 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30981 window.location.href = this.href;
30996 * @class Roo.bootstrap.Brick
30997 * @extends Roo.bootstrap.Component
30998 * Bootstrap Brick class
31001 * Create a new Brick
31002 * @param {Object} config The config object
31005 Roo.bootstrap.Brick = function(config){
31006 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31012 * When a Brick is click
31013 * @param {Roo.bootstrap.Brick} this
31014 * @param {Roo.EventObject} e
31020 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
31023 * @cfg {String} title
31027 * @cfg {String} html
31031 * @cfg {String} bgimage
31035 * @cfg {String} cls
31039 * @cfg {String} href
31043 * @cfg {String} video
31047 * @cfg {Boolean} square
31051 getAutoCreate : function()
31053 var cls = 'roo-brick';
31055 if(this.href.length){
31056 cls += ' roo-brick-link';
31059 if(this.bgimage.length){
31060 cls += ' roo-brick-image';
31063 if(!this.html.length && !this.bgimage.length){
31064 cls += ' roo-brick-center-title';
31067 if(!this.html.length && this.bgimage.length){
31068 cls += ' roo-brick-bottom-title';
31072 cls += ' ' + this.cls;
31076 tag: (this.href.length) ? 'a' : 'div',
31081 cls: 'roo-brick-paragraph',
31087 if(this.href.length){
31088 cfg.href = this.href;
31091 var cn = cfg.cn[0].cn;
31093 if(this.title.length){
31096 cls: 'roo-brick-title',
31101 if(this.html.length){
31104 cls: 'roo-brick-text',
31111 if(this.bgimage.length){
31114 cls: 'roo-brick-image-view',
31122 initEvents: function()
31124 if(this.title.length || this.html.length){
31125 this.el.on('mouseenter' ,this.enter, this);
31126 this.el.on('mouseleave', this.leave, this);
31130 Roo.EventManager.onWindowResize(this.resize, this);
31135 resize : function()
31137 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31139 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31140 // paragraph.setHeight(paragraph.getWidth());
31142 if(this.bgimage.length){
31143 var image = this.el.select('.roo-brick-image-view', true).first();
31144 image.setWidth(paragraph.getWidth());
31145 image.setHeight(paragraph.getWidth());
31150 enter: function(e, el)
31152 e.preventDefault();
31154 if(this.bgimage.length){
31155 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31156 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31160 leave: function(e, el)
31162 e.preventDefault();
31164 if(this.bgimage.length){
31165 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31166 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31176 * Ext JS Library 1.1.1
31177 * Copyright(c) 2006-2007, Ext JS, LLC.
31179 * Originally Released Under LGPL - original licence link has changed is not relivant.
31182 * <script type="text/javascript">
31187 * @class Roo.bootstrap.SplitBar
31188 * @extends Roo.util.Observable
31189 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31193 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31194 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31195 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31196 split.minSize = 100;
31197 split.maxSize = 600;
31198 split.animate = true;
31199 split.on('moved', splitterMoved);
31202 * Create a new SplitBar
31203 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
31204 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
31205 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31206 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
31207 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31208 position of the SplitBar).
31210 Roo.bootstrap.SplitBar = function(cfg){
31215 // dragElement : elm
31216 // resizingElement: el,
31218 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31219 // placement : Roo.bootstrap.SplitBar.LEFT ,
31220 // existingProxy ???
31223 this.el = Roo.get(cfg.dragElement, true);
31224 this.el.dom.unselectable = "on";
31226 this.resizingEl = Roo.get(cfg.resizingElement, true);
31230 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31231 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31234 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31237 * The minimum size of the resizing element. (Defaults to 0)
31243 * The maximum size of the resizing element. (Defaults to 2000)
31246 this.maxSize = 2000;
31249 * Whether to animate the transition to the new size
31252 this.animate = false;
31255 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31258 this.useShim = false;
31263 if(!cfg.existingProxy){
31265 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31267 this.proxy = Roo.get(cfg.existingProxy).dom;
31270 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31273 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31276 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31279 this.dragSpecs = {};
31282 * @private The adapter to use to positon and resize elements
31284 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31285 this.adapter.init(this);
31287 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31289 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31290 this.el.addClass("roo-splitbar-h");
31293 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31294 this.el.addClass("roo-splitbar-v");
31300 * Fires when the splitter is moved (alias for {@link #event-moved})
31301 * @param {Roo.bootstrap.SplitBar} this
31302 * @param {Number} newSize the new width or height
31307 * Fires when the splitter is moved
31308 * @param {Roo.bootstrap.SplitBar} this
31309 * @param {Number} newSize the new width or height
31313 * @event beforeresize
31314 * Fires before the splitter is dragged
31315 * @param {Roo.bootstrap.SplitBar} this
31317 "beforeresize" : true,
31319 "beforeapply" : true
31322 Roo.util.Observable.call(this);
31325 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31326 onStartProxyDrag : function(x, y){
31327 this.fireEvent("beforeresize", this);
31329 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
31331 o.enableDisplayMode("block");
31332 // all splitbars share the same overlay
31333 Roo.bootstrap.SplitBar.prototype.overlay = o;
31335 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31336 this.overlay.show();
31337 Roo.get(this.proxy).setDisplayed("block");
31338 var size = this.adapter.getElementSize(this);
31339 this.activeMinSize = this.getMinimumSize();;
31340 this.activeMaxSize = this.getMaximumSize();;
31341 var c1 = size - this.activeMinSize;
31342 var c2 = Math.max(this.activeMaxSize - size, 0);
31343 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31344 this.dd.resetConstraints();
31345 this.dd.setXConstraint(
31346 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
31347 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31349 this.dd.setYConstraint(0, 0);
31351 this.dd.resetConstraints();
31352 this.dd.setXConstraint(0, 0);
31353 this.dd.setYConstraint(
31354 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
31355 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31358 this.dragSpecs.startSize = size;
31359 this.dragSpecs.startPoint = [x, y];
31360 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31364 * @private Called after the drag operation by the DDProxy
31366 onEndProxyDrag : function(e){
31367 Roo.get(this.proxy).setDisplayed(false);
31368 var endPoint = Roo.lib.Event.getXY(e);
31370 this.overlay.hide();
31373 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31374 newSize = this.dragSpecs.startSize +
31375 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31376 endPoint[0] - this.dragSpecs.startPoint[0] :
31377 this.dragSpecs.startPoint[0] - endPoint[0]
31380 newSize = this.dragSpecs.startSize +
31381 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31382 endPoint[1] - this.dragSpecs.startPoint[1] :
31383 this.dragSpecs.startPoint[1] - endPoint[1]
31386 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31387 if(newSize != this.dragSpecs.startSize){
31388 if(this.fireEvent('beforeapply', this, newSize) !== false){
31389 this.adapter.setElementSize(this, newSize);
31390 this.fireEvent("moved", this, newSize);
31391 this.fireEvent("resize", this, newSize);
31397 * Get the adapter this SplitBar uses
31398 * @return The adapter object
31400 getAdapter : function(){
31401 return this.adapter;
31405 * Set the adapter this SplitBar uses
31406 * @param {Object} adapter A SplitBar adapter object
31408 setAdapter : function(adapter){
31409 this.adapter = adapter;
31410 this.adapter.init(this);
31414 * Gets the minimum size for the resizing element
31415 * @return {Number} The minimum size
31417 getMinimumSize : function(){
31418 return this.minSize;
31422 * Sets the minimum size for the resizing element
31423 * @param {Number} minSize The minimum size
31425 setMinimumSize : function(minSize){
31426 this.minSize = minSize;
31430 * Gets the maximum size for the resizing element
31431 * @return {Number} The maximum size
31433 getMaximumSize : function(){
31434 return this.maxSize;
31438 * Sets the maximum size for the resizing element
31439 * @param {Number} maxSize The maximum size
31441 setMaximumSize : function(maxSize){
31442 this.maxSize = maxSize;
31446 * Sets the initialize size for the resizing element
31447 * @param {Number} size The initial size
31449 setCurrentSize : function(size){
31450 var oldAnimate = this.animate;
31451 this.animate = false;
31452 this.adapter.setElementSize(this, size);
31453 this.animate = oldAnimate;
31457 * Destroy this splitbar.
31458 * @param {Boolean} removeEl True to remove the element
31460 destroy : function(removeEl){
31462 this.shim.remove();
31465 this.proxy.parentNode.removeChild(this.proxy);
31473 * @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.
31475 Roo.bootstrap.SplitBar.createProxy = function(dir){
31476 var proxy = new Roo.Element(document.createElement("div"));
31477 proxy.unselectable();
31478 var cls = 'roo-splitbar-proxy';
31479 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31480 document.body.appendChild(proxy.dom);
31485 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31486 * Default Adapter. It assumes the splitter and resizing element are not positioned
31487 * elements and only gets/sets the width of the element. Generally used for table based layouts.
31489 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31492 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31493 // do nothing for now
31494 init : function(s){
31498 * Called before drag operations to get the current size of the resizing element.
31499 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31501 getElementSize : function(s){
31502 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31503 return s.resizingEl.getWidth();
31505 return s.resizingEl.getHeight();
31510 * Called after drag operations to set the size of the resizing element.
31511 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31512 * @param {Number} newSize The new size to set
31513 * @param {Function} onComplete A function to be invoked when resizing is complete
31515 setElementSize : function(s, newSize, onComplete){
31516 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31518 s.resizingEl.setWidth(newSize);
31520 onComplete(s, newSize);
31523 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31528 s.resizingEl.setHeight(newSize);
31530 onComplete(s, newSize);
31533 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31540 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31541 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31542 * Adapter that moves the splitter element to align with the resized sizing element.
31543 * Used with an absolute positioned SplitBar.
31544 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31545 * document.body, make sure you assign an id to the body element.
31547 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31548 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31549 this.container = Roo.get(container);
31552 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31553 init : function(s){
31554 this.basic.init(s);
31557 getElementSize : function(s){
31558 return this.basic.getElementSize(s);
31561 setElementSize : function(s, newSize, onComplete){
31562 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31565 moveSplitter : function(s){
31566 var yes = Roo.bootstrap.SplitBar;
31567 switch(s.placement){
31569 s.el.setX(s.resizingEl.getRight());
31572 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31575 s.el.setY(s.resizingEl.getBottom());
31578 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31585 * Orientation constant - Create a vertical SplitBar
31589 Roo.bootstrap.SplitBar.VERTICAL = 1;
31592 * Orientation constant - Create a horizontal SplitBar
31596 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31599 * Placement constant - The resizing element is to the left of the splitter element
31603 Roo.bootstrap.SplitBar.LEFT = 1;
31606 * Placement constant - The resizing element is to the right of the splitter element
31610 Roo.bootstrap.SplitBar.RIGHT = 2;
31613 * Placement constant - The resizing element is positioned above the splitter element
31617 Roo.bootstrap.SplitBar.TOP = 3;
31620 * Placement constant - The resizing element is positioned under splitter element
31624 Roo.bootstrap.SplitBar.BOTTOM = 4;
31625 Roo.namespace("Roo.bootstrap.layout");/*
31627 * Ext JS Library 1.1.1
31628 * Copyright(c) 2006-2007, Ext JS, LLC.
31630 * Originally Released Under LGPL - original licence link has changed is not relivant.
31633 * <script type="text/javascript">
31637 * @class Roo.bootstrap.layout.Manager
31638 * @extends Roo.bootstrap.Component
31639 * Base class for layout managers.
31641 Roo.bootstrap.layout.Manager = function(config)
31643 Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31649 /** false to disable window resize monitoring @type Boolean */
31650 this.monitorWindowResize = true;
31655 * Fires when a layout is performed.
31656 * @param {Roo.LayoutManager} this
31660 * @event regionresized
31661 * Fires when the user resizes a region.
31662 * @param {Roo.LayoutRegion} region The resized region
31663 * @param {Number} newSize The new size (width for east/west, height for north/south)
31665 "regionresized" : true,
31667 * @event regioncollapsed
31668 * Fires when a region is collapsed.
31669 * @param {Roo.LayoutRegion} region The collapsed region
31671 "regioncollapsed" : true,
31673 * @event regionexpanded
31674 * Fires when a region is expanded.
31675 * @param {Roo.LayoutRegion} region The expanded region
31677 "regionexpanded" : true
31679 this.updating = false;
31682 this.el = Roo.get(config.el);
31688 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31693 monitorWindowResize : true,
31699 onRender : function(ct, position)
31702 this.el = Roo.get(ct);
31708 initEvents: function()
31712 // ie scrollbar fix
31713 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31714 document.body.scroll = "no";
31715 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31716 this.el.position('relative');
31718 this.id = this.el.id;
31719 this.el.addClass("roo-layout-container");
31720 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31721 if(this.el.dom != document.body ) {
31722 this.el.on('resize', this.layout,this);
31723 this.el.on('show', this.layout,this);
31729 * Returns true if this layout is currently being updated
31730 * @return {Boolean}
31732 isUpdating : function(){
31733 return this.updating;
31737 * Suspend the LayoutManager from doing auto-layouts while
31738 * making multiple add or remove calls
31740 beginUpdate : function(){
31741 this.updating = true;
31745 * Restore auto-layouts and optionally disable the manager from performing a layout
31746 * @param {Boolean} noLayout true to disable a layout update
31748 endUpdate : function(noLayout){
31749 this.updating = false;
31755 layout: function(){
31759 onRegionResized : function(region, newSize){
31760 this.fireEvent("regionresized", region, newSize);
31764 onRegionCollapsed : function(region){
31765 this.fireEvent("regioncollapsed", region);
31768 onRegionExpanded : function(region){
31769 this.fireEvent("regionexpanded", region);
31773 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31774 * performs box-model adjustments.
31775 * @return {Object} The size as an object {width: (the width), height: (the height)}
31777 getViewSize : function()
31780 if(this.el.dom != document.body){
31781 size = this.el.getSize();
31783 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31785 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31786 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31791 * Returns the Element this layout is bound to.
31792 * @return {Roo.Element}
31794 getEl : function(){
31799 * Returns the specified region.
31800 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31801 * @return {Roo.LayoutRegion}
31803 getRegion : function(target){
31804 return this.regions[target.toLowerCase()];
31807 onWindowResize : function(){
31808 if(this.monitorWindowResize){
31814 * Ext JS Library 1.1.1
31815 * Copyright(c) 2006-2007, Ext JS, LLC.
31817 * Originally Released Under LGPL - original licence link has changed is not relivant.
31820 * <script type="text/javascript">
31823 * @class Roo.bootstrap.layout.Border
31824 * @extends Roo.bootstrap.layout.Manager
31825 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31826 * please see: examples/bootstrap/nested.html<br><br>
31828 <b>The container the layout is rendered into can be either the body element or any other element.
31829 If it is not the body element, the container needs to either be an absolute positioned element,
31830 or you will need to add "position:relative" to the css of the container. You will also need to specify
31831 the container size if it is not the body element.</b>
31834 * Create a new Border
31835 * @param {Object} config Configuration options
31837 Roo.bootstrap.layout.Border = function(config){
31838 config = config || {};
31839 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31843 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31844 if(config[region]){
31845 config[region].region = region;
31846 this.addRegion(config[region]);
31852 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
31854 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31856 * Creates and adds a new region if it doesn't already exist.
31857 * @param {String} target The target region key (north, south, east, west or center).
31858 * @param {Object} config The regions config object
31859 * @return {BorderLayoutRegion} The new region
31861 addRegion : function(config)
31863 if(!this.regions[config.region]){
31864 var r = this.factory(config);
31865 this.bindRegion(r);
31867 return this.regions[config.region];
31871 bindRegion : function(r){
31872 this.regions[r.config.region] = r;
31874 r.on("visibilitychange", this.layout, this);
31875 r.on("paneladded", this.layout, this);
31876 r.on("panelremoved", this.layout, this);
31877 r.on("invalidated", this.layout, this);
31878 r.on("resized", this.onRegionResized, this);
31879 r.on("collapsed", this.onRegionCollapsed, this);
31880 r.on("expanded", this.onRegionExpanded, this);
31884 * Performs a layout update.
31886 layout : function()
31888 if(this.updating) {
31892 // render all the rebions if they have not been done alreayd?
31893 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31894 if(this.regions[region] && !this.regions[region].bodyEl){
31895 this.regions[region].onRender(this.el)
31899 var size = this.getViewSize();
31900 var w = size.width;
31901 var h = size.height;
31906 //var x = 0, y = 0;
31908 var rs = this.regions;
31909 var north = rs["north"];
31910 var south = rs["south"];
31911 var west = rs["west"];
31912 var east = rs["east"];
31913 var center = rs["center"];
31914 //if(this.hideOnLayout){ // not supported anymore
31915 //c.el.setStyle("display", "none");
31917 if(north && north.isVisible()){
31918 var b = north.getBox();
31919 var m = north.getMargins();
31920 b.width = w - (m.left+m.right);
31923 centerY = b.height + b.y + m.bottom;
31924 centerH -= centerY;
31925 north.updateBox(this.safeBox(b));
31927 if(south && south.isVisible()){
31928 var b = south.getBox();
31929 var m = south.getMargins();
31930 b.width = w - (m.left+m.right);
31932 var totalHeight = (b.height + m.top + m.bottom);
31933 b.y = h - totalHeight + m.top;
31934 centerH -= totalHeight;
31935 south.updateBox(this.safeBox(b));
31937 if(west && west.isVisible()){
31938 var b = west.getBox();
31939 var m = west.getMargins();
31940 b.height = centerH - (m.top+m.bottom);
31942 b.y = centerY + m.top;
31943 var totalWidth = (b.width + m.left + m.right);
31944 centerX += totalWidth;
31945 centerW -= totalWidth;
31946 west.updateBox(this.safeBox(b));
31948 if(east && east.isVisible()){
31949 var b = east.getBox();
31950 var m = east.getMargins();
31951 b.height = centerH - (m.top+m.bottom);
31952 var totalWidth = (b.width + m.left + m.right);
31953 b.x = w - totalWidth + m.left;
31954 b.y = centerY + m.top;
31955 centerW -= totalWidth;
31956 east.updateBox(this.safeBox(b));
31959 var m = center.getMargins();
31961 x: centerX + m.left,
31962 y: centerY + m.top,
31963 width: centerW - (m.left+m.right),
31964 height: centerH - (m.top+m.bottom)
31966 //if(this.hideOnLayout){
31967 //center.el.setStyle("display", "block");
31969 center.updateBox(this.safeBox(centerBox));
31972 this.fireEvent("layout", this);
31976 safeBox : function(box){
31977 box.width = Math.max(0, box.width);
31978 box.height = Math.max(0, box.height);
31983 * Adds a ContentPanel (or subclass) to this layout.
31984 * @param {String} target The target region key (north, south, east, west or center).
31985 * @param {Roo.ContentPanel} panel The panel to add
31986 * @return {Roo.ContentPanel} The added panel
31988 add : function(target, panel){
31990 target = target.toLowerCase();
31991 return this.regions[target].add(panel);
31995 * Remove a ContentPanel (or subclass) to this layout.
31996 * @param {String} target The target region key (north, south, east, west or center).
31997 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31998 * @return {Roo.ContentPanel} The removed panel
32000 remove : function(target, panel){
32001 target = target.toLowerCase();
32002 return this.regions[target].remove(panel);
32006 * Searches all regions for a panel with the specified id
32007 * @param {String} panelId
32008 * @return {Roo.ContentPanel} The panel or null if it wasn't found
32010 findPanel : function(panelId){
32011 var rs = this.regions;
32012 for(var target in rs){
32013 if(typeof rs[target] != "function"){
32014 var p = rs[target].getPanel(panelId);
32024 * Searches all regions for a panel with the specified id and activates (shows) it.
32025 * @param {String/ContentPanel} panelId The panels id or the panel itself
32026 * @return {Roo.ContentPanel} The shown panel or null
32028 showPanel : function(panelId) {
32029 var rs = this.regions;
32030 for(var target in rs){
32031 var r = rs[target];
32032 if(typeof r != "function"){
32033 if(r.hasPanel(panelId)){
32034 return r.showPanel(panelId);
32042 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32043 * @param {Roo.state.Provider} provider (optional) An alternate state provider
32046 restoreState : function(provider){
32048 provider = Roo.state.Manager;
32050 var sm = new Roo.LayoutStateManager();
32051 sm.init(this, provider);
32057 * Adds a xtype elements to the layout.
32061 xtype : 'ContentPanel',
32068 xtype : 'NestedLayoutPanel',
32074 items : [ ... list of content panels or nested layout panels.. ]
32078 * @param {Object} cfg Xtype definition of item to add.
32080 addxtype : function(cfg)
32082 // basically accepts a pannel...
32083 // can accept a layout region..!?!?
32084 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32087 // theory? children can only be panels??
32089 //if (!cfg.xtype.match(/Panel$/)) {
32094 if (typeof(cfg.region) == 'undefined') {
32095 Roo.log("Failed to add Panel, region was not set");
32099 var region = cfg.region;
32105 xitems = cfg.items;
32112 case 'Content': // ContentPanel (el, cfg)
32113 case 'Scroll': // ContentPanel (el, cfg)
32115 cfg.autoCreate = true;
32116 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32118 // var el = this.el.createChild();
32119 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32122 this.add(region, ret);
32126 case 'TreePanel': // our new panel!
32127 cfg.el = this.el.createChild();
32128 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32129 this.add(region, ret);
32134 // create a new Layout (which is a Border Layout...
32136 var clayout = cfg.layout;
32137 clayout.el = this.el.createChild();
32138 clayout.items = clayout.items || [];
32142 // replace this exitems with the clayout ones..
32143 xitems = clayout.items;
32145 // force background off if it's in center...
32146 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32147 cfg.background = false;
32149 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
32152 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32153 //console.log('adding nested layout panel ' + cfg.toSource());
32154 this.add(region, ret);
32155 nb = {}; /// find first...
32160 // needs grid and region
32162 //var el = this.getRegion(region).el.createChild();
32164 *var el = this.el.createChild();
32165 // create the grid first...
32166 cfg.grid.container = el;
32167 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
32170 if (region == 'center' && this.active ) {
32171 cfg.background = false;
32174 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32176 this.add(region, ret);
32178 if (cfg.background) {
32179 // render grid on panel activation (if panel background)
32180 ret.on('activate', function(gp) {
32181 if (!gp.grid.rendered) {
32182 // gp.grid.render(el);
32186 // cfg.grid.render(el);
32192 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
32193 // it was the old xcomponent building that caused this before.
32194 // espeically if border is the top element in the tree.
32204 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32206 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32207 this.add(region, ret);
32211 throw "Can not add '" + cfg.xtype + "' to Border";
32217 this.beginUpdate();
32221 Roo.each(xitems, function(i) {
32222 region = nb && i.region ? i.region : false;
32224 var add = ret.addxtype(i);
32227 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32228 if (!i.background) {
32229 abn[region] = nb[region] ;
32236 // make the last non-background panel active..
32237 //if (nb) { Roo.log(abn); }
32240 for(var r in abn) {
32241 region = this.getRegion(r);
32243 // tried using nb[r], but it does not work..
32245 region.showPanel(abn[r]);
32256 factory : function(cfg)
32259 var validRegions = Roo.bootstrap.layout.Border.regions;
32261 var target = cfg.region;
32264 var r = Roo.bootstrap.layout;
32268 return new r.North(cfg);
32270 return new r.South(cfg);
32272 return new r.East(cfg);
32274 return new r.West(cfg);
32276 return new r.Center(cfg);
32278 throw 'Layout region "'+target+'" not supported.';
32285 * Ext JS Library 1.1.1
32286 * Copyright(c) 2006-2007, Ext JS, LLC.
32288 * Originally Released Under LGPL - original licence link has changed is not relivant.
32291 * <script type="text/javascript">
32295 * @class Roo.bootstrap.layout.Basic
32296 * @extends Roo.util.Observable
32297 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32298 * and does not have a titlebar, tabs or any other features. All it does is size and position
32299 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32300 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32301 * @cfg {string} region the region that it inhabits..
32302 * @cfg {bool} skipConfig skip config?
32306 Roo.bootstrap.layout.Basic = function(config){
32308 this.mgr = config.mgr;
32310 this.position = config.region;
32312 var skipConfig = config.skipConfig;
32316 * @scope Roo.BasicLayoutRegion
32320 * @event beforeremove
32321 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32322 * @param {Roo.LayoutRegion} this
32323 * @param {Roo.ContentPanel} panel The panel
32324 * @param {Object} e The cancel event object
32326 "beforeremove" : true,
32328 * @event invalidated
32329 * Fires when the layout for this region is changed.
32330 * @param {Roo.LayoutRegion} this
32332 "invalidated" : true,
32334 * @event visibilitychange
32335 * Fires when this region is shown or hidden
32336 * @param {Roo.LayoutRegion} this
32337 * @param {Boolean} visibility true or false
32339 "visibilitychange" : true,
32341 * @event paneladded
32342 * Fires when a panel is added.
32343 * @param {Roo.LayoutRegion} this
32344 * @param {Roo.ContentPanel} panel The panel
32346 "paneladded" : true,
32348 * @event panelremoved
32349 * Fires when a panel is removed.
32350 * @param {Roo.LayoutRegion} this
32351 * @param {Roo.ContentPanel} panel The panel
32353 "panelremoved" : true,
32355 * @event beforecollapse
32356 * Fires when this region before collapse.
32357 * @param {Roo.LayoutRegion} this
32359 "beforecollapse" : true,
32362 * Fires when this region is collapsed.
32363 * @param {Roo.LayoutRegion} this
32365 "collapsed" : true,
32368 * Fires when this region is expanded.
32369 * @param {Roo.LayoutRegion} this
32374 * Fires when this region is slid into view.
32375 * @param {Roo.LayoutRegion} this
32377 "slideshow" : true,
32380 * Fires when this region slides out of view.
32381 * @param {Roo.LayoutRegion} this
32383 "slidehide" : true,
32385 * @event panelactivated
32386 * Fires when a panel is activated.
32387 * @param {Roo.LayoutRegion} this
32388 * @param {Roo.ContentPanel} panel The activated panel
32390 "panelactivated" : true,
32393 * Fires when the user resizes this region.
32394 * @param {Roo.LayoutRegion} this
32395 * @param {Number} newSize The new size (width for east/west, height for north/south)
32399 /** A collection of panels in this region. @type Roo.util.MixedCollection */
32400 this.panels = new Roo.util.MixedCollection();
32401 this.panels.getKey = this.getPanelId.createDelegate(this);
32403 this.activePanel = null;
32404 // ensure listeners are added...
32406 if (config.listeners || config.events) {
32407 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32408 listeners : config.listeners || {},
32409 events : config.events || {}
32413 if(skipConfig !== true){
32414 this.applyConfig(config);
32418 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32420 getPanelId : function(p){
32424 applyConfig : function(config){
32425 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32426 this.config = config;
32431 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
32432 * the width, for horizontal (north, south) the height.
32433 * @param {Number} newSize The new width or height
32435 resizeTo : function(newSize){
32436 var el = this.el ? this.el :
32437 (this.activePanel ? this.activePanel.getEl() : null);
32439 switch(this.position){
32442 el.setWidth(newSize);
32443 this.fireEvent("resized", this, newSize);
32447 el.setHeight(newSize);
32448 this.fireEvent("resized", this, newSize);
32454 getBox : function(){
32455 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32458 getMargins : function(){
32459 return this.margins;
32462 updateBox : function(box){
32464 var el = this.activePanel.getEl();
32465 el.dom.style.left = box.x + "px";
32466 el.dom.style.top = box.y + "px";
32467 this.activePanel.setSize(box.width, box.height);
32471 * Returns the container element for this region.
32472 * @return {Roo.Element}
32474 getEl : function(){
32475 return this.activePanel;
32479 * Returns true if this region is currently visible.
32480 * @return {Boolean}
32482 isVisible : function(){
32483 return this.activePanel ? true : false;
32486 setActivePanel : function(panel){
32487 panel = this.getPanel(panel);
32488 if(this.activePanel && this.activePanel != panel){
32489 this.activePanel.setActiveState(false);
32490 this.activePanel.getEl().setLeftTop(-10000,-10000);
32492 this.activePanel = panel;
32493 panel.setActiveState(true);
32495 panel.setSize(this.box.width, this.box.height);
32497 this.fireEvent("panelactivated", this, panel);
32498 this.fireEvent("invalidated");
32502 * Show the specified panel.
32503 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32504 * @return {Roo.ContentPanel} The shown panel or null
32506 showPanel : function(panel){
32507 panel = this.getPanel(panel);
32509 this.setActivePanel(panel);
32515 * Get the active panel for this region.
32516 * @return {Roo.ContentPanel} The active panel or null
32518 getActivePanel : function(){
32519 return this.activePanel;
32523 * Add the passed ContentPanel(s)
32524 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32525 * @return {Roo.ContentPanel} The panel added (if only one was added)
32527 add : function(panel){
32528 if(arguments.length > 1){
32529 for(var i = 0, len = arguments.length; i < len; i++) {
32530 this.add(arguments[i]);
32534 if(this.hasPanel(panel)){
32535 this.showPanel(panel);
32538 var el = panel.getEl();
32539 if(el.dom.parentNode != this.mgr.el.dom){
32540 this.mgr.el.dom.appendChild(el.dom);
32542 if(panel.setRegion){
32543 panel.setRegion(this);
32545 this.panels.add(panel);
32546 el.setStyle("position", "absolute");
32547 if(!panel.background){
32548 this.setActivePanel(panel);
32549 if(this.config.initialSize && this.panels.getCount()==1){
32550 this.resizeTo(this.config.initialSize);
32553 this.fireEvent("paneladded", this, panel);
32558 * Returns true if the panel is in this region.
32559 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32560 * @return {Boolean}
32562 hasPanel : function(panel){
32563 if(typeof panel == "object"){ // must be panel obj
32564 panel = panel.getId();
32566 return this.getPanel(panel) ? true : false;
32570 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32571 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32572 * @param {Boolean} preservePanel Overrides the config preservePanel option
32573 * @return {Roo.ContentPanel} The panel that was removed
32575 remove : function(panel, preservePanel){
32576 panel = this.getPanel(panel);
32581 this.fireEvent("beforeremove", this, panel, e);
32582 if(e.cancel === true){
32585 var panelId = panel.getId();
32586 this.panels.removeKey(panelId);
32591 * Returns the panel specified or null if it's not in this region.
32592 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32593 * @return {Roo.ContentPanel}
32595 getPanel : function(id){
32596 if(typeof id == "object"){ // must be panel obj
32599 return this.panels.get(id);
32603 * Returns this regions position (north/south/east/west/center).
32606 getPosition: function(){
32607 return this.position;
32611 * Ext JS Library 1.1.1
32612 * Copyright(c) 2006-2007, Ext JS, LLC.
32614 * Originally Released Under LGPL - original licence link has changed is not relivant.
32617 * <script type="text/javascript">
32621 * @class Roo.bootstrap.layout.Region
32622 * @extends Roo.bootstrap.layout.Basic
32623 * This class represents a region in a layout manager.
32625 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32626 * @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})
32627 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
32628 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
32629 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
32630 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
32631 * @cfg {String} title The title for the region (overrides panel titles)
32632 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
32633 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32634 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
32635 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32636 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
32637 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32638 * the space available, similar to FireFox 1.5 tabs (defaults to false)
32639 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
32640 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
32641 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
32643 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
32644 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
32645 * @cfg {Boolean} disableTabTips True to disable tab tooltips
32646 * @cfg {Number} width For East/West panels
32647 * @cfg {Number} height For North/South panels
32648 * @cfg {Boolean} split To show the splitter
32649 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
32651 * @cfg {string} cls Extra CSS classes to add to region
32653 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32654 * @cfg {string} region the region that it inhabits..
32657 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
32658 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
32660 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
32661 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
32662 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
32664 Roo.bootstrap.layout.Region = function(config)
32666 this.applyConfig(config);
32668 var mgr = config.mgr;
32669 var pos = config.region;
32670 config.skipConfig = true;
32671 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32674 this.onRender(mgr.el);
32677 this.visible = true;
32678 this.collapsed = false;
32679 this.unrendered_panels = [];
32682 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32684 position: '', // set by wrapper (eg. north/south etc..)
32685 unrendered_panels : null, // unrendered panels.
32686 createBody : function(){
32687 /** This region's body element
32688 * @type Roo.Element */
32689 this.bodyEl = this.el.createChild({
32691 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32695 onRender: function(ctr, pos)
32697 var dh = Roo.DomHelper;
32698 /** This region's container element
32699 * @type Roo.Element */
32700 this.el = dh.append(ctr.dom, {
32702 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32704 /** This region's title element
32705 * @type Roo.Element */
32707 this.titleEl = dh.append(this.el.dom,
32710 unselectable: "on",
32711 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32713 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
32714 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32717 this.titleEl.enableDisplayMode();
32718 /** This region's title text element
32719 * @type HTMLElement */
32720 this.titleTextEl = this.titleEl.dom.firstChild;
32721 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32723 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32724 this.closeBtn.enableDisplayMode();
32725 this.closeBtn.on("click", this.closeClicked, this);
32726 this.closeBtn.hide();
32728 this.createBody(this.config);
32729 if(this.config.hideWhenEmpty){
32731 this.on("paneladded", this.validateVisibility, this);
32732 this.on("panelremoved", this.validateVisibility, this);
32734 if(this.autoScroll){
32735 this.bodyEl.setStyle("overflow", "auto");
32737 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32739 //if(c.titlebar !== false){
32740 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32741 this.titleEl.hide();
32743 this.titleEl.show();
32744 if(this.config.title){
32745 this.titleTextEl.innerHTML = this.config.title;
32749 if(this.config.collapsed){
32750 this.collapse(true);
32752 if(this.config.hidden){
32756 if (this.unrendered_panels && this.unrendered_panels.length) {
32757 for (var i =0;i< this.unrendered_panels.length; i++) {
32758 this.add(this.unrendered_panels[i]);
32760 this.unrendered_panels = null;
32766 applyConfig : function(c)
32769 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32770 var dh = Roo.DomHelper;
32771 if(c.titlebar !== false){
32772 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32773 this.collapseBtn.on("click", this.collapse, this);
32774 this.collapseBtn.enableDisplayMode();
32776 if(c.showPin === true || this.showPin){
32777 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32778 this.stickBtn.enableDisplayMode();
32779 this.stickBtn.on("click", this.expand, this);
32780 this.stickBtn.hide();
32785 /** This region's collapsed element
32786 * @type Roo.Element */
32789 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32790 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32793 if(c.floatable !== false){
32794 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32795 this.collapsedEl.on("click", this.collapseClick, this);
32798 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32799 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32800 id: "message", unselectable: "on", style:{"float":"left"}});
32801 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32803 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32804 this.expandBtn.on("click", this.expand, this);
32808 if(this.collapseBtn){
32809 this.collapseBtn.setVisible(c.collapsible == true);
32812 this.cmargins = c.cmargins || this.cmargins ||
32813 (this.position == "west" || this.position == "east" ?
32814 {top: 0, left: 2, right:2, bottom: 0} :
32815 {top: 2, left: 0, right:0, bottom: 2});
32817 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32820 this.bottomTabs = c.tabPosition != "top";
32822 this.autoScroll = c.autoScroll || false;
32827 this.duration = c.duration || .30;
32828 this.slideDuration = c.slideDuration || .45;
32833 * Returns true if this region is currently visible.
32834 * @return {Boolean}
32836 isVisible : function(){
32837 return this.visible;
32841 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32842 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
32844 //setCollapsedTitle : function(title){
32845 // title = title || " ";
32846 // if(this.collapsedTitleTextEl){
32847 // this.collapsedTitleTextEl.innerHTML = title;
32851 getBox : function(){
32853 // if(!this.collapsed){
32854 b = this.el.getBox(false, true);
32856 // b = this.collapsedEl.getBox(false, true);
32861 getMargins : function(){
32862 return this.margins;
32863 //return this.collapsed ? this.cmargins : this.margins;
32866 highlight : function(){
32867 this.el.addClass("x-layout-panel-dragover");
32870 unhighlight : function(){
32871 this.el.removeClass("x-layout-panel-dragover");
32874 updateBox : function(box)
32876 if (!this.bodyEl) {
32877 return; // not rendered yet..
32881 if(!this.collapsed){
32882 this.el.dom.style.left = box.x + "px";
32883 this.el.dom.style.top = box.y + "px";
32884 this.updateBody(box.width, box.height);
32886 this.collapsedEl.dom.style.left = box.x + "px";
32887 this.collapsedEl.dom.style.top = box.y + "px";
32888 this.collapsedEl.setSize(box.width, box.height);
32891 this.tabs.autoSizeTabs();
32895 updateBody : function(w, h)
32898 this.el.setWidth(w);
32899 w -= this.el.getBorderWidth("rl");
32900 if(this.config.adjustments){
32901 w += this.config.adjustments[0];
32904 if(h !== null && h > 0){
32905 this.el.setHeight(h);
32906 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32907 h -= this.el.getBorderWidth("tb");
32908 if(this.config.adjustments){
32909 h += this.config.adjustments[1];
32911 this.bodyEl.setHeight(h);
32913 h = this.tabs.syncHeight(h);
32916 if(this.panelSize){
32917 w = w !== null ? w : this.panelSize.width;
32918 h = h !== null ? h : this.panelSize.height;
32920 if(this.activePanel){
32921 var el = this.activePanel.getEl();
32922 w = w !== null ? w : el.getWidth();
32923 h = h !== null ? h : el.getHeight();
32924 this.panelSize = {width: w, height: h};
32925 this.activePanel.setSize(w, h);
32927 if(Roo.isIE && this.tabs){
32928 this.tabs.el.repaint();
32933 * Returns the container element for this region.
32934 * @return {Roo.Element}
32936 getEl : function(){
32941 * Hides this region.
32944 //if(!this.collapsed){
32945 this.el.dom.style.left = "-2000px";
32948 // this.collapsedEl.dom.style.left = "-2000px";
32949 // this.collapsedEl.hide();
32951 this.visible = false;
32952 this.fireEvent("visibilitychange", this, false);
32956 * Shows this region if it was previously hidden.
32959 //if(!this.collapsed){
32962 // this.collapsedEl.show();
32964 this.visible = true;
32965 this.fireEvent("visibilitychange", this, true);
32968 closeClicked : function(){
32969 if(this.activePanel){
32970 this.remove(this.activePanel);
32974 collapseClick : function(e){
32976 e.stopPropagation();
32979 e.stopPropagation();
32985 * Collapses this region.
32986 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32989 collapse : function(skipAnim, skipCheck = false){
32990 if(this.collapsed) {
32994 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32996 this.collapsed = true;
32998 this.split.el.hide();
33000 if(this.config.animate && skipAnim !== true){
33001 this.fireEvent("invalidated", this);
33002 this.animateCollapse();
33004 this.el.setLocation(-20000,-20000);
33006 this.collapsedEl.show();
33007 this.fireEvent("collapsed", this);
33008 this.fireEvent("invalidated", this);
33014 animateCollapse : function(){
33019 * Expands this region if it was previously collapsed.
33020 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33021 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33024 expand : function(e, skipAnim){
33026 e.stopPropagation();
33028 if(!this.collapsed || this.el.hasActiveFx()) {
33032 this.afterSlideIn();
33035 this.collapsed = false;
33036 if(this.config.animate && skipAnim !== true){
33037 this.animateExpand();
33041 this.split.el.show();
33043 this.collapsedEl.setLocation(-2000,-2000);
33044 this.collapsedEl.hide();
33045 this.fireEvent("invalidated", this);
33046 this.fireEvent("expanded", this);
33050 animateExpand : function(){
33054 initTabs : function()
33056 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33058 var ts = new Roo.bootstrap.panel.Tabs({
33059 el: this.bodyEl.dom,
33060 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33061 disableTooltips: this.config.disableTabTips,
33062 toolbar : this.config.toolbar
33065 if(this.config.hideTabs){
33066 ts.stripWrap.setDisplayed(false);
33069 ts.resizeTabs = this.config.resizeTabs === true;
33070 ts.minTabWidth = this.config.minTabWidth || 40;
33071 ts.maxTabWidth = this.config.maxTabWidth || 250;
33072 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33073 ts.monitorResize = false;
33074 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33075 ts.bodyEl.addClass('roo-layout-tabs-body');
33076 this.panels.each(this.initPanelAsTab, this);
33079 initPanelAsTab : function(panel){
33080 var ti = this.tabs.addTab(
33084 this.config.closeOnTab && panel.isClosable()
33086 if(panel.tabTip !== undefined){
33087 ti.setTooltip(panel.tabTip);
33089 ti.on("activate", function(){
33090 this.setActivePanel(panel);
33093 if(this.config.closeOnTab){
33094 ti.on("beforeclose", function(t, e){
33096 this.remove(panel);
33102 updatePanelTitle : function(panel, title)
33104 if(this.activePanel == panel){
33105 this.updateTitle(title);
33108 var ti = this.tabs.getTab(panel.getEl().id);
33110 if(panel.tabTip !== undefined){
33111 ti.setTooltip(panel.tabTip);
33116 updateTitle : function(title){
33117 if(this.titleTextEl && !this.config.title){
33118 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
33122 setActivePanel : function(panel)
33124 panel = this.getPanel(panel);
33125 if(this.activePanel && this.activePanel != panel){
33126 this.activePanel.setActiveState(false);
33128 this.activePanel = panel;
33129 panel.setActiveState(true);
33130 if(this.panelSize){
33131 panel.setSize(this.panelSize.width, this.panelSize.height);
33134 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33136 this.updateTitle(panel.getTitle());
33138 this.fireEvent("invalidated", this);
33140 this.fireEvent("panelactivated", this, panel);
33144 * Shows the specified panel.
33145 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33146 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33148 showPanel : function(panel)
33150 panel = this.getPanel(panel);
33153 var tab = this.tabs.getTab(panel.getEl().id);
33154 if(tab.isHidden()){
33155 this.tabs.unhideTab(tab.id);
33159 this.setActivePanel(panel);
33166 * Get the active panel for this region.
33167 * @return {Roo.ContentPanel} The active panel or null
33169 getActivePanel : function(){
33170 return this.activePanel;
33173 validateVisibility : function(){
33174 if(this.panels.getCount() < 1){
33175 this.updateTitle(" ");
33176 this.closeBtn.hide();
33179 if(!this.isVisible()){
33186 * Adds the passed ContentPanel(s) to this region.
33187 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33188 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33190 add : function(panel)
33192 if(arguments.length > 1){
33193 for(var i = 0, len = arguments.length; i < len; i++) {
33194 this.add(arguments[i]);
33199 // if we have not been rendered yet, then we can not really do much of this..
33200 if (!this.bodyEl) {
33201 this.unrendered_panels.push(panel);
33208 if(this.hasPanel(panel)){
33209 this.showPanel(panel);
33212 panel.setRegion(this);
33213 this.panels.add(panel);
33214 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33215 // sinle panel - no tab...?? would it not be better to render it with the tabs,
33216 // and hide them... ???
33217 this.bodyEl.dom.appendChild(panel.getEl().dom);
33218 if(panel.background !== true){
33219 this.setActivePanel(panel);
33221 this.fireEvent("paneladded", this, panel);
33228 this.initPanelAsTab(panel);
33232 if(panel.background !== true){
33233 this.tabs.activate(panel.getEl().id);
33235 this.fireEvent("paneladded", this, panel);
33240 * Hides the tab for the specified panel.
33241 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33243 hidePanel : function(panel){
33244 if(this.tabs && (panel = this.getPanel(panel))){
33245 this.tabs.hideTab(panel.getEl().id);
33250 * Unhides the tab for a previously hidden panel.
33251 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33253 unhidePanel : function(panel){
33254 if(this.tabs && (panel = this.getPanel(panel))){
33255 this.tabs.unhideTab(panel.getEl().id);
33259 clearPanels : function(){
33260 while(this.panels.getCount() > 0){
33261 this.remove(this.panels.first());
33266 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33267 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33268 * @param {Boolean} preservePanel Overrides the config preservePanel option
33269 * @return {Roo.ContentPanel} The panel that was removed
33271 remove : function(panel, preservePanel)
33273 panel = this.getPanel(panel);
33278 this.fireEvent("beforeremove", this, panel, e);
33279 if(e.cancel === true){
33282 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33283 var panelId = panel.getId();
33284 this.panels.removeKey(panelId);
33286 document.body.appendChild(panel.getEl().dom);
33289 this.tabs.removeTab(panel.getEl().id);
33290 }else if (!preservePanel){
33291 this.bodyEl.dom.removeChild(panel.getEl().dom);
33293 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33294 var p = this.panels.first();
33295 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33296 tempEl.appendChild(p.getEl().dom);
33297 this.bodyEl.update("");
33298 this.bodyEl.dom.appendChild(p.getEl().dom);
33300 this.updateTitle(p.getTitle());
33302 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33303 this.setActivePanel(p);
33305 panel.setRegion(null);
33306 if(this.activePanel == panel){
33307 this.activePanel = null;
33309 if(this.config.autoDestroy !== false && preservePanel !== true){
33310 try{panel.destroy();}catch(e){}
33312 this.fireEvent("panelremoved", this, panel);
33317 * Returns the TabPanel component used by this region
33318 * @return {Roo.TabPanel}
33320 getTabs : function(){
33324 createTool : function(parentEl, className){
33325 var btn = Roo.DomHelper.append(parentEl, {
33327 cls: "x-layout-tools-button",
33330 cls: "roo-layout-tools-button-inner " + className,
33334 btn.addClassOnOver("roo-layout-tools-button-over");
33339 * Ext JS Library 1.1.1
33340 * Copyright(c) 2006-2007, Ext JS, LLC.
33342 * Originally Released Under LGPL - original licence link has changed is not relivant.
33345 * <script type="text/javascript">
33351 * @class Roo.SplitLayoutRegion
33352 * @extends Roo.LayoutRegion
33353 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33355 Roo.bootstrap.layout.Split = function(config){
33356 this.cursor = config.cursor;
33357 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33360 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33362 splitTip : "Drag to resize.",
33363 collapsibleSplitTip : "Drag to resize. Double click to hide.",
33364 useSplitTips : false,
33366 applyConfig : function(config){
33367 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33370 onRender : function(ctr,pos) {
33372 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33373 if(!this.config.split){
33378 var splitEl = Roo.DomHelper.append(ctr.dom, {
33380 id: this.el.id + "-split",
33381 cls: "roo-layout-split roo-layout-split-"+this.position,
33384 /** The SplitBar for this region
33385 * @type Roo.SplitBar */
33386 // does not exist yet...
33387 Roo.log([this.position, this.orientation]);
33389 this.split = new Roo.bootstrap.SplitBar({
33390 dragElement : splitEl,
33391 resizingElement: this.el,
33392 orientation : this.orientation
33395 this.split.on("moved", this.onSplitMove, this);
33396 this.split.useShim = this.config.useShim === true;
33397 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33398 if(this.useSplitTips){
33399 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33401 //if(config.collapsible){
33402 // this.split.el.on("dblclick", this.collapse, this);
33405 if(typeof this.config.minSize != "undefined"){
33406 this.split.minSize = this.config.minSize;
33408 if(typeof this.config.maxSize != "undefined"){
33409 this.split.maxSize = this.config.maxSize;
33411 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33412 this.hideSplitter();
33417 getHMaxSize : function(){
33418 var cmax = this.config.maxSize || 10000;
33419 var center = this.mgr.getRegion("center");
33420 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33423 getVMaxSize : function(){
33424 var cmax = this.config.maxSize || 10000;
33425 var center = this.mgr.getRegion("center");
33426 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33429 onSplitMove : function(split, newSize){
33430 this.fireEvent("resized", this, newSize);
33434 * Returns the {@link Roo.SplitBar} for this region.
33435 * @return {Roo.SplitBar}
33437 getSplitBar : function(){
33442 this.hideSplitter();
33443 Roo.bootstrap.layout.Split.superclass.hide.call(this);
33446 hideSplitter : function(){
33448 this.split.el.setLocation(-2000,-2000);
33449 this.split.el.hide();
33455 this.split.el.show();
33457 Roo.bootstrap.layout.Split.superclass.show.call(this);
33460 beforeSlide: function(){
33461 if(Roo.isGecko){// firefox overflow auto bug workaround
33462 this.bodyEl.clip();
33464 this.tabs.bodyEl.clip();
33466 if(this.activePanel){
33467 this.activePanel.getEl().clip();
33469 if(this.activePanel.beforeSlide){
33470 this.activePanel.beforeSlide();
33476 afterSlide : function(){
33477 if(Roo.isGecko){// firefox overflow auto bug workaround
33478 this.bodyEl.unclip();
33480 this.tabs.bodyEl.unclip();
33482 if(this.activePanel){
33483 this.activePanel.getEl().unclip();
33484 if(this.activePanel.afterSlide){
33485 this.activePanel.afterSlide();
33491 initAutoHide : function(){
33492 if(this.autoHide !== false){
33493 if(!this.autoHideHd){
33494 var st = new Roo.util.DelayedTask(this.slideIn, this);
33495 this.autoHideHd = {
33496 "mouseout": function(e){
33497 if(!e.within(this.el, true)){
33501 "mouseover" : function(e){
33507 this.el.on(this.autoHideHd);
33511 clearAutoHide : function(){
33512 if(this.autoHide !== false){
33513 this.el.un("mouseout", this.autoHideHd.mouseout);
33514 this.el.un("mouseover", this.autoHideHd.mouseover);
33518 clearMonitor : function(){
33519 Roo.get(document).un("click", this.slideInIf, this);
33522 // these names are backwards but not changed for compat
33523 slideOut : function(){
33524 if(this.isSlid || this.el.hasActiveFx()){
33527 this.isSlid = true;
33528 if(this.collapseBtn){
33529 this.collapseBtn.hide();
33531 this.closeBtnState = this.closeBtn.getStyle('display');
33532 this.closeBtn.hide();
33534 this.stickBtn.show();
33537 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33538 this.beforeSlide();
33539 this.el.setStyle("z-index", 10001);
33540 this.el.slideIn(this.getSlideAnchor(), {
33541 callback: function(){
33543 this.initAutoHide();
33544 Roo.get(document).on("click", this.slideInIf, this);
33545 this.fireEvent("slideshow", this);
33552 afterSlideIn : function(){
33553 this.clearAutoHide();
33554 this.isSlid = false;
33555 this.clearMonitor();
33556 this.el.setStyle("z-index", "");
33557 if(this.collapseBtn){
33558 this.collapseBtn.show();
33560 this.closeBtn.setStyle('display', this.closeBtnState);
33562 this.stickBtn.hide();
33564 this.fireEvent("slidehide", this);
33567 slideIn : function(cb){
33568 if(!this.isSlid || this.el.hasActiveFx()){
33572 this.isSlid = false;
33573 this.beforeSlide();
33574 this.el.slideOut(this.getSlideAnchor(), {
33575 callback: function(){
33576 this.el.setLeftTop(-10000, -10000);
33578 this.afterSlideIn();
33586 slideInIf : function(e){
33587 if(!e.within(this.el)){
33592 animateCollapse : function(){
33593 this.beforeSlide();
33594 this.el.setStyle("z-index", 20000);
33595 var anchor = this.getSlideAnchor();
33596 this.el.slideOut(anchor, {
33597 callback : function(){
33598 this.el.setStyle("z-index", "");
33599 this.collapsedEl.slideIn(anchor, {duration:.3});
33601 this.el.setLocation(-10000,-10000);
33603 this.fireEvent("collapsed", this);
33610 animateExpand : function(){
33611 this.beforeSlide();
33612 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33613 this.el.setStyle("z-index", 20000);
33614 this.collapsedEl.hide({
33617 this.el.slideIn(this.getSlideAnchor(), {
33618 callback : function(){
33619 this.el.setStyle("z-index", "");
33622 this.split.el.show();
33624 this.fireEvent("invalidated", this);
33625 this.fireEvent("expanded", this);
33653 getAnchor : function(){
33654 return this.anchors[this.position];
33657 getCollapseAnchor : function(){
33658 return this.canchors[this.position];
33661 getSlideAnchor : function(){
33662 return this.sanchors[this.position];
33665 getAlignAdj : function(){
33666 var cm = this.cmargins;
33667 switch(this.position){
33683 getExpandAdj : function(){
33684 var c = this.collapsedEl, cm = this.cmargins;
33685 switch(this.position){
33687 return [-(cm.right+c.getWidth()+cm.left), 0];
33690 return [cm.right+c.getWidth()+cm.left, 0];
33693 return [0, -(cm.top+cm.bottom+c.getHeight())];
33696 return [0, cm.top+cm.bottom+c.getHeight()];
33702 * Ext JS Library 1.1.1
33703 * Copyright(c) 2006-2007, Ext JS, LLC.
33705 * Originally Released Under LGPL - original licence link has changed is not relivant.
33708 * <script type="text/javascript">
33711 * These classes are private internal classes
33713 Roo.bootstrap.layout.Center = function(config){
33714 config.region = "center";
33715 Roo.bootstrap.layout.Region.call(this, config);
33716 this.visible = true;
33717 this.minWidth = config.minWidth || 20;
33718 this.minHeight = config.minHeight || 20;
33721 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33723 // center panel can't be hidden
33727 // center panel can't be hidden
33730 getMinWidth: function(){
33731 return this.minWidth;
33734 getMinHeight: function(){
33735 return this.minHeight;
33748 Roo.bootstrap.layout.North = function(config)
33750 config.region = 'north';
33751 config.cursor = 'n-resize';
33753 Roo.bootstrap.layout.Split.call(this, config);
33757 this.split.placement = Roo.bootstrap.SplitBar.TOP;
33758 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33759 this.split.el.addClass("roo-layout-split-v");
33761 var size = config.initialSize || config.height;
33762 if(typeof size != "undefined"){
33763 this.el.setHeight(size);
33766 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33768 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33772 getBox : function(){
33773 if(this.collapsed){
33774 return this.collapsedEl.getBox();
33776 var box = this.el.getBox();
33778 box.height += this.split.el.getHeight();
33783 updateBox : function(box){
33784 if(this.split && !this.collapsed){
33785 box.height -= this.split.el.getHeight();
33786 this.split.el.setLeft(box.x);
33787 this.split.el.setTop(box.y+box.height);
33788 this.split.el.setWidth(box.width);
33790 if(this.collapsed){
33791 this.updateBody(box.width, null);
33793 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33801 Roo.bootstrap.layout.South = function(config){
33802 config.region = 'south';
33803 config.cursor = 's-resize';
33804 Roo.bootstrap.layout.Split.call(this, config);
33806 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33807 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33808 this.split.el.addClass("roo-layout-split-v");
33810 var size = config.initialSize || config.height;
33811 if(typeof size != "undefined"){
33812 this.el.setHeight(size);
33816 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33817 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33818 getBox : function(){
33819 if(this.collapsed){
33820 return this.collapsedEl.getBox();
33822 var box = this.el.getBox();
33824 var sh = this.split.el.getHeight();
33831 updateBox : function(box){
33832 if(this.split && !this.collapsed){
33833 var sh = this.split.el.getHeight();
33836 this.split.el.setLeft(box.x);
33837 this.split.el.setTop(box.y-sh);
33838 this.split.el.setWidth(box.width);
33840 if(this.collapsed){
33841 this.updateBody(box.width, null);
33843 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33847 Roo.bootstrap.layout.East = function(config){
33848 config.region = "east";
33849 config.cursor = "e-resize";
33850 Roo.bootstrap.layout.Split.call(this, config);
33852 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33853 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33854 this.split.el.addClass("roo-layout-split-h");
33856 var size = config.initialSize || config.width;
33857 if(typeof size != "undefined"){
33858 this.el.setWidth(size);
33861 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33862 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33863 getBox : function(){
33864 if(this.collapsed){
33865 return this.collapsedEl.getBox();
33867 var box = this.el.getBox();
33869 var sw = this.split.el.getWidth();
33876 updateBox : function(box){
33877 if(this.split && !this.collapsed){
33878 var sw = this.split.el.getWidth();
33880 this.split.el.setLeft(box.x);
33881 this.split.el.setTop(box.y);
33882 this.split.el.setHeight(box.height);
33885 if(this.collapsed){
33886 this.updateBody(null, box.height);
33888 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33892 Roo.bootstrap.layout.West = function(config){
33893 config.region = "west";
33894 config.cursor = "w-resize";
33896 Roo.bootstrap.layout.Split.call(this, config);
33898 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33899 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33900 this.split.el.addClass("roo-layout-split-h");
33904 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33905 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33907 onRender: function(ctr, pos)
33909 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33910 var size = this.config.initialSize || this.config.width;
33911 if(typeof size != "undefined"){
33912 this.el.setWidth(size);
33916 getBox : function(){
33917 if(this.collapsed){
33918 return this.collapsedEl.getBox();
33920 var box = this.el.getBox();
33922 box.width += this.split.el.getWidth();
33927 updateBox : function(box){
33928 if(this.split && !this.collapsed){
33929 var sw = this.split.el.getWidth();
33931 this.split.el.setLeft(box.x+box.width);
33932 this.split.el.setTop(box.y);
33933 this.split.el.setHeight(box.height);
33935 if(this.collapsed){
33936 this.updateBody(null, box.height);
33938 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33941 Roo.namespace("Roo.bootstrap.panel");/*
33943 * Ext JS Library 1.1.1
33944 * Copyright(c) 2006-2007, Ext JS, LLC.
33946 * Originally Released Under LGPL - original licence link has changed is not relivant.
33949 * <script type="text/javascript">
33952 * @class Roo.ContentPanel
33953 * @extends Roo.util.Observable
33954 * A basic ContentPanel element.
33955 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
33956 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
33957 * @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
33958 * @cfg {Boolean} closable True if the panel can be closed/removed
33959 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
33960 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33961 * @cfg {Toolbar} toolbar A toolbar for this panel
33962 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
33963 * @cfg {String} title The title for this panel
33964 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33965 * @cfg {String} url Calls {@link #setUrl} with this value
33966 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33967 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
33968 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
33969 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
33972 * Create a new ContentPanel.
33973 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33974 * @param {String/Object} config A string to set only the title or a config object
33975 * @param {String} content (optional) Set the HTML content for this panel
33976 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33978 Roo.bootstrap.panel.Content = function( config){
33980 var el = config.el;
33981 var content = config.content;
33983 if(config.autoCreate){ // xtype is available if this is called from factory
33986 this.el = Roo.get(el);
33987 if(!this.el && config && config.autoCreate){
33988 if(typeof config.autoCreate == "object"){
33989 if(!config.autoCreate.id){
33990 config.autoCreate.id = config.id||el;
33992 this.el = Roo.DomHelper.append(document.body,
33993 config.autoCreate, true);
33995 var elcfg = { tag: "div",
33996 cls: "roo-layout-inactive-content",
34000 elcfg.html = config.html;
34004 this.el = Roo.DomHelper.append(document.body, elcfg , true);
34007 this.closable = false;
34008 this.loaded = false;
34009 this.active = false;
34012 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
34014 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
34016 this.wrapEl = this.el.wrap();
34018 if (config.toolbar.items) {
34019 ti = config.toolbar.items ;
34020 delete config.toolbar.items ;
34024 this.toolbar.render(this.wrapEl, 'before');
34025 for(var i =0;i < ti.length;i++) {
34026 // Roo.log(['add child', items[i]]);
34027 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34029 this.toolbar.items = nitems;
34030 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34031 delete config.toolbar;
34035 // xtype created footer. - not sure if will work as we normally have to render first..
34036 if (this.footer && !this.footer.el && this.footer.xtype) {
34037 if (!this.wrapEl) {
34038 this.wrapEl = this.el.wrap();
34041 this.footer.container = this.wrapEl.createChild();
34043 this.footer = Roo.factory(this.footer, Roo);
34048 if(typeof config == "string"){
34049 this.title = config;
34051 Roo.apply(this, config);
34055 this.resizeEl = Roo.get(this.resizeEl, true);
34057 this.resizeEl = this.el;
34059 // handle view.xtype
34067 * Fires when this panel is activated.
34068 * @param {Roo.ContentPanel} this
34072 * @event deactivate
34073 * Fires when this panel is activated.
34074 * @param {Roo.ContentPanel} this
34076 "deactivate" : true,
34080 * Fires when this panel is resized if fitToFrame is true.
34081 * @param {Roo.ContentPanel} this
34082 * @param {Number} width The width after any component adjustments
34083 * @param {Number} height The height after any component adjustments
34089 * Fires when this tab is created
34090 * @param {Roo.ContentPanel} this
34101 if(this.autoScroll){
34102 this.resizeEl.setStyle("overflow", "auto");
34104 // fix randome scrolling
34105 //this.el.on('scroll', function() {
34106 // Roo.log('fix random scolling');
34107 // this.scrollTo('top',0);
34110 content = content || this.content;
34112 this.setContent(content);
34114 if(config && config.url){
34115 this.setUrl(this.url, this.params, this.loadOnce);
34120 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34122 if (this.view && typeof(this.view.xtype) != 'undefined') {
34123 this.view.el = this.el.appendChild(document.createElement("div"));
34124 this.view = Roo.factory(this.view);
34125 this.view.render && this.view.render(false, '');
34129 this.fireEvent('render', this);
34132 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34134 setRegion : function(region){
34135 this.region = region;
34137 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
34139 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
34144 * Returns the toolbar for this Panel if one was configured.
34145 * @return {Roo.Toolbar}
34147 getToolbar : function(){
34148 return this.toolbar;
34151 setActiveState : function(active){
34152 this.active = active;
34154 this.fireEvent("deactivate", this);
34156 this.fireEvent("activate", this);
34160 * Updates this panel's element
34161 * @param {String} content The new content
34162 * @param {Boolean} loadScripts (optional) true to look for and process scripts
34164 setContent : function(content, loadScripts){
34165 this.el.update(content, loadScripts);
34168 ignoreResize : function(w, h){
34169 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34172 this.lastSize = {width: w, height: h};
34177 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34178 * @return {Roo.UpdateManager} The UpdateManager
34180 getUpdateManager : function(){
34181 return this.el.getUpdateManager();
34184 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34185 * @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:
34188 url: "your-url.php",
34189 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34190 callback: yourFunction,
34191 scope: yourObject, //(optional scope)
34194 text: "Loading...",
34199 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34200 * 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.
34201 * @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}
34202 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34203 * @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.
34204 * @return {Roo.ContentPanel} this
34207 var um = this.el.getUpdateManager();
34208 um.update.apply(um, arguments);
34214 * 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.
34215 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34216 * @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)
34217 * @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)
34218 * @return {Roo.UpdateManager} The UpdateManager
34220 setUrl : function(url, params, loadOnce){
34221 if(this.refreshDelegate){
34222 this.removeListener("activate", this.refreshDelegate);
34224 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34225 this.on("activate", this.refreshDelegate);
34226 return this.el.getUpdateManager();
34229 _handleRefresh : function(url, params, loadOnce){
34230 if(!loadOnce || !this.loaded){
34231 var updater = this.el.getUpdateManager();
34232 updater.update(url, params, this._setLoaded.createDelegate(this));
34236 _setLoaded : function(){
34237 this.loaded = true;
34241 * Returns this panel's id
34244 getId : function(){
34249 * Returns this panel's element - used by regiosn to add.
34250 * @return {Roo.Element}
34252 getEl : function(){
34253 return this.wrapEl || this.el;
34258 adjustForComponents : function(width, height)
34260 //Roo.log('adjustForComponents ');
34261 if(this.resizeEl != this.el){
34262 width -= this.el.getFrameWidth('lr');
34263 height -= this.el.getFrameWidth('tb');
34266 var te = this.toolbar.getEl();
34267 height -= te.getHeight();
34268 te.setWidth(width);
34271 var te = this.footer.getEl();
34272 Roo.log("footer:" + te.getHeight());
34274 height -= te.getHeight();
34275 te.setWidth(width);
34279 if(this.adjustments){
34280 width += this.adjustments[0];
34281 height += this.adjustments[1];
34283 return {"width": width, "height": height};
34286 setSize : function(width, height){
34287 if(this.fitToFrame && !this.ignoreResize(width, height)){
34288 if(this.fitContainer && this.resizeEl != this.el){
34289 this.el.setSize(width, height);
34291 var size = this.adjustForComponents(width, height);
34292 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34293 this.fireEvent('resize', this, size.width, size.height);
34298 * Returns this panel's title
34301 getTitle : function(){
34306 * Set this panel's title
34307 * @param {String} title
34309 setTitle : function(title){
34310 this.title = title;
34312 this.region.updatePanelTitle(this, title);
34317 * Returns true is this panel was configured to be closable
34318 * @return {Boolean}
34320 isClosable : function(){
34321 return this.closable;
34324 beforeSlide : function(){
34326 this.resizeEl.clip();
34329 afterSlide : function(){
34331 this.resizeEl.unclip();
34335 * Force a content refresh from the URL specified in the {@link #setUrl} method.
34336 * Will fail silently if the {@link #setUrl} method has not been called.
34337 * This does not activate the panel, just updates its content.
34339 refresh : function(){
34340 if(this.refreshDelegate){
34341 this.loaded = false;
34342 this.refreshDelegate();
34347 * Destroys this panel
34349 destroy : function(){
34350 this.el.removeAllListeners();
34351 var tempEl = document.createElement("span");
34352 tempEl.appendChild(this.el.dom);
34353 tempEl.innerHTML = "";
34359 * form - if the content panel contains a form - this is a reference to it.
34360 * @type {Roo.form.Form}
34364 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34365 * This contains a reference to it.
34371 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34381 * @param {Object} cfg Xtype definition of item to add.
34385 getChildContainer: function () {
34386 return this.getEl();
34391 var ret = new Roo.factory(cfg);
34396 if (cfg.xtype.match(/^Form$/)) {
34399 //if (this.footer) {
34400 // el = this.footer.container.insertSibling(false, 'before');
34402 el = this.el.createChild();
34405 this.form = new Roo.form.Form(cfg);
34408 if ( this.form.allItems.length) {
34409 this.form.render(el.dom);
34413 // should only have one of theses..
34414 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34415 // views.. should not be just added - used named prop 'view''
34417 cfg.el = this.el.appendChild(document.createElement("div"));
34420 var ret = new Roo.factory(cfg);
34422 ret.render && ret.render(false, ''); // render blank..
34432 * @class Roo.bootstrap.panel.Grid
34433 * @extends Roo.bootstrap.panel.Content
34435 * Create a new GridPanel.
34436 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34437 * @param {Object} config A the config object
34443 Roo.bootstrap.panel.Grid = function(config)
34447 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34448 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34450 config.el = this.wrapper;
34451 //this.el = this.wrapper;
34453 if (config.container) {
34454 // ctor'ed from a Border/panel.grid
34457 this.wrapper.setStyle("overflow", "hidden");
34458 this.wrapper.addClass('roo-grid-container');
34463 if(config.toolbar){
34464 var tool_el = this.wrapper.createChild();
34465 this.toolbar = Roo.factory(config.toolbar);
34467 if (config.toolbar.items) {
34468 ti = config.toolbar.items ;
34469 delete config.toolbar.items ;
34473 this.toolbar.render(tool_el);
34474 for(var i =0;i < ti.length;i++) {
34475 // Roo.log(['add child', items[i]]);
34476 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34478 this.toolbar.items = nitems;
34480 delete config.toolbar;
34483 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34484 config.grid.scrollBody = true;;
34485 config.grid.monitorWindowResize = false; // turn off autosizing
34486 config.grid.autoHeight = false;
34487 config.grid.autoWidth = false;
34489 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34491 if (config.background) {
34492 // render grid on panel activation (if panel background)
34493 this.on('activate', function(gp) {
34494 if (!gp.grid.rendered) {
34495 gp.grid.render(el);
34496 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34502 this.grid.render(this.wrapper);
34503 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34506 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34507 // ??? needed ??? config.el = this.wrapper;
34512 // xtype created footer. - not sure if will work as we normally have to render first..
34513 if (this.footer && !this.footer.el && this.footer.xtype) {
34515 var ctr = this.grid.getView().getFooterPanel(true);
34516 this.footer.dataSource = this.grid.dataSource;
34517 this.footer = Roo.factory(this.footer, Roo);
34518 this.footer.render(ctr);
34528 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34529 getId : function(){
34530 return this.grid.id;
34534 * Returns the grid for this panel
34535 * @return {Roo.bootstrap.Table}
34537 getGrid : function(){
34541 setSize : function(width, height){
34542 if(!this.ignoreResize(width, height)){
34543 var grid = this.grid;
34544 var size = this.adjustForComponents(width, height);
34545 var gridel = grid.getGridEl();
34546 gridel.setSize(size.width, size.height);
34548 var thd = grid.getGridEl().select('thead',true).first();
34549 var tbd = grid.getGridEl().select('tbody', true).first();
34551 tbd.setSize(width, height - thd.getHeight());
34560 beforeSlide : function(){
34561 this.grid.getView().scroller.clip();
34564 afterSlide : function(){
34565 this.grid.getView().scroller.unclip();
34568 destroy : function(){
34569 this.grid.destroy();
34571 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
34576 * @class Roo.bootstrap.panel.Nest
34577 * @extends Roo.bootstrap.panel.Content
34579 * Create a new Panel, that can contain a layout.Border.
34582 * @param {Roo.BorderLayout} layout The layout for this panel
34583 * @param {String/Object} config A string to set only the title or a config object
34585 Roo.bootstrap.panel.Nest = function(config)
34587 // construct with only one argument..
34588 /* FIXME - implement nicer consturctors
34589 if (layout.layout) {
34591 layout = config.layout;
34592 delete config.layout;
34594 if (layout.xtype && !layout.getEl) {
34595 // then layout needs constructing..
34596 layout = Roo.factory(layout, Roo);
34600 config.el = config.layout.getEl();
34602 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34604 config.layout.monitorWindowResize = false; // turn off autosizing
34605 this.layout = config.layout;
34606 this.layout.getEl().addClass("roo-layout-nested-layout");
34613 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34615 setSize : function(width, height){
34616 if(!this.ignoreResize(width, height)){
34617 var size = this.adjustForComponents(width, height);
34618 var el = this.layout.getEl();
34619 if (size.height < 1) {
34620 el.setWidth(size.width);
34622 el.setSize(size.width, size.height);
34624 var touch = el.dom.offsetWidth;
34625 this.layout.layout();
34626 // ie requires a double layout on the first pass
34627 if(Roo.isIE && !this.initialized){
34628 this.initialized = true;
34629 this.layout.layout();
34634 // activate all subpanels if not currently active..
34636 setActiveState : function(active){
34637 this.active = active;
34639 this.fireEvent("deactivate", this);
34643 this.fireEvent("activate", this);
34644 // not sure if this should happen before or after..
34645 if (!this.layout) {
34646 return; // should not happen..
34649 for (var r in this.layout.regions) {
34650 reg = this.layout.getRegion(r);
34651 if (reg.getActivePanel()) {
34652 //reg.showPanel(reg.getActivePanel()); // force it to activate..
34653 reg.setActivePanel(reg.getActivePanel());
34656 if (!reg.panels.length) {
34659 reg.showPanel(reg.getPanel(0));
34668 * Returns the nested BorderLayout for this panel
34669 * @return {Roo.BorderLayout}
34671 getLayout : function(){
34672 return this.layout;
34676 * Adds a xtype elements to the layout of the nested panel
34680 xtype : 'ContentPanel',
34687 xtype : 'NestedLayoutPanel',
34693 items : [ ... list of content panels or nested layout panels.. ]
34697 * @param {Object} cfg Xtype definition of item to add.
34699 addxtype : function(cfg) {
34700 return this.layout.addxtype(cfg);
34705 * Ext JS Library 1.1.1
34706 * Copyright(c) 2006-2007, Ext JS, LLC.
34708 * Originally Released Under LGPL - original licence link has changed is not relivant.
34711 * <script type="text/javascript">
34714 * @class Roo.TabPanel
34715 * @extends Roo.util.Observable
34716 * A lightweight tab container.
34720 // basic tabs 1, built from existing content
34721 var tabs = new Roo.TabPanel("tabs1");
34722 tabs.addTab("script", "View Script");
34723 tabs.addTab("markup", "View Markup");
34724 tabs.activate("script");
34726 // more advanced tabs, built from javascript
34727 var jtabs = new Roo.TabPanel("jtabs");
34728 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34730 // set up the UpdateManager
34731 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34732 var updater = tab2.getUpdateManager();
34733 updater.setDefaultUrl("ajax1.htm");
34734 tab2.on('activate', updater.refresh, updater, true);
34736 // Use setUrl for Ajax loading
34737 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34738 tab3.setUrl("ajax2.htm", null, true);
34741 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34744 jtabs.activate("jtabs-1");
34747 * Create a new TabPanel.
34748 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34749 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34751 Roo.bootstrap.panel.Tabs = function(config){
34753 * The container element for this TabPanel.
34754 * @type Roo.Element
34756 this.el = Roo.get(config.el);
34759 if(typeof config == "boolean"){
34760 this.tabPosition = config ? "bottom" : "top";
34762 Roo.apply(this, config);
34766 if(this.tabPosition == "bottom"){
34767 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34768 this.el.addClass("roo-tabs-bottom");
34770 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34771 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34772 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34774 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34776 if(this.tabPosition != "bottom"){
34777 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34778 * @type Roo.Element
34780 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34781 this.el.addClass("roo-tabs-top");
34785 this.bodyEl.setStyle("position", "relative");
34787 this.active = null;
34788 this.activateDelegate = this.activate.createDelegate(this);
34793 * Fires when the active tab changes
34794 * @param {Roo.TabPanel} this
34795 * @param {Roo.TabPanelItem} activePanel The new active tab
34799 * @event beforetabchange
34800 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34801 * @param {Roo.TabPanel} this
34802 * @param {Object} e Set cancel to true on this object to cancel the tab change
34803 * @param {Roo.TabPanelItem} tab The tab being changed to
34805 "beforetabchange" : true
34808 Roo.EventManager.onWindowResize(this.onResize, this);
34809 this.cpad = this.el.getPadding("lr");
34810 this.hiddenCount = 0;
34813 // toolbar on the tabbar support...
34814 if (this.toolbar) {
34815 alert("no toolbar support yet");
34816 this.toolbar = false;
34818 var tcfg = this.toolbar;
34819 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
34820 this.toolbar = new Roo.Toolbar(tcfg);
34821 if (Roo.isSafari) {
34822 var tbl = tcfg.container.child('table', true);
34823 tbl.setAttribute('width', '100%');
34831 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34834 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34836 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34838 tabPosition : "top",
34840 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34842 currentTabWidth : 0,
34844 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34848 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34852 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34854 preferredTabWidth : 175,
34856 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34858 resizeTabs : false,
34860 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34862 monitorResize : true,
34864 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
34869 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34870 * @param {String} id The id of the div to use <b>or create</b>
34871 * @param {String} text The text for the tab
34872 * @param {String} content (optional) Content to put in the TabPanelItem body
34873 * @param {Boolean} closable (optional) True to create a close icon on the tab
34874 * @return {Roo.TabPanelItem} The created TabPanelItem
34876 addTab : function(id, text, content, closable)
34878 var item = new Roo.bootstrap.panel.TabItem({
34882 closable : closable
34884 this.addTabItem(item);
34886 item.setContent(content);
34892 * Returns the {@link Roo.TabPanelItem} with the specified id/index
34893 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34894 * @return {Roo.TabPanelItem}
34896 getTab : function(id){
34897 return this.items[id];
34901 * Hides the {@link Roo.TabPanelItem} with the specified id/index
34902 * @param {String/Number} id The id or index of the TabPanelItem to hide.
34904 hideTab : function(id){
34905 var t = this.items[id];
34908 this.hiddenCount++;
34909 this.autoSizeTabs();
34914 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34915 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34917 unhideTab : function(id){
34918 var t = this.items[id];
34920 t.setHidden(false);
34921 this.hiddenCount--;
34922 this.autoSizeTabs();
34927 * Adds an existing {@link Roo.TabPanelItem}.
34928 * @param {Roo.TabPanelItem} item The TabPanelItem to add
34930 addTabItem : function(item){
34931 this.items[item.id] = item;
34932 this.items.push(item);
34933 // if(this.resizeTabs){
34934 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34935 // this.autoSizeTabs();
34937 // item.autoSize();
34942 * Removes a {@link Roo.TabPanelItem}.
34943 * @param {String/Number} id The id or index of the TabPanelItem to remove.
34945 removeTab : function(id){
34946 var items = this.items;
34947 var tab = items[id];
34948 if(!tab) { return; }
34949 var index = items.indexOf(tab);
34950 if(this.active == tab && items.length > 1){
34951 var newTab = this.getNextAvailable(index);
34956 this.stripEl.dom.removeChild(tab.pnode.dom);
34957 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34958 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34960 items.splice(index, 1);
34961 delete this.items[tab.id];
34962 tab.fireEvent("close", tab);
34963 tab.purgeListeners();
34964 this.autoSizeTabs();
34967 getNextAvailable : function(start){
34968 var items = this.items;
34970 // look for a next tab that will slide over to
34971 // replace the one being removed
34972 while(index < items.length){
34973 var item = items[++index];
34974 if(item && !item.isHidden()){
34978 // if one isn't found select the previous tab (on the left)
34981 var item = items[--index];
34982 if(item && !item.isHidden()){
34990 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34991 * @param {String/Number} id The id or index of the TabPanelItem to disable.
34993 disableTab : function(id){
34994 var tab = this.items[id];
34995 if(tab && this.active != tab){
35001 * Enables a {@link Roo.TabPanelItem} that is disabled.
35002 * @param {String/Number} id The id or index of the TabPanelItem to enable.
35004 enableTab : function(id){
35005 var tab = this.items[id];
35010 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
35011 * @param {String/Number} id The id or index of the TabPanelItem to activate.
35012 * @return {Roo.TabPanelItem} The TabPanelItem.
35014 activate : function(id){
35015 var tab = this.items[id];
35019 if(tab == this.active || tab.disabled){
35023 this.fireEvent("beforetabchange", this, e, tab);
35024 if(e.cancel !== true && !tab.disabled){
35026 this.active.hide();
35028 this.active = this.items[id];
35029 this.active.show();
35030 this.fireEvent("tabchange", this, this.active);
35036 * Gets the active {@link Roo.TabPanelItem}.
35037 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35039 getActiveTab : function(){
35040 return this.active;
35044 * Updates the tab body element to fit the height of the container element
35045 * for overflow scrolling
35046 * @param {Number} targetHeight (optional) Override the starting height from the elements height
35048 syncHeight : function(targetHeight){
35049 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35050 var bm = this.bodyEl.getMargins();
35051 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35052 this.bodyEl.setHeight(newHeight);
35056 onResize : function(){
35057 if(this.monitorResize){
35058 this.autoSizeTabs();
35063 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35065 beginUpdate : function(){
35066 this.updating = true;
35070 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35072 endUpdate : function(){
35073 this.updating = false;
35074 this.autoSizeTabs();
35078 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35080 autoSizeTabs : function(){
35081 var count = this.items.length;
35082 var vcount = count - this.hiddenCount;
35083 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35086 var w = Math.max(this.el.getWidth() - this.cpad, 10);
35087 var availWidth = Math.floor(w / vcount);
35088 var b = this.stripBody;
35089 if(b.getWidth() > w){
35090 var tabs = this.items;
35091 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35092 if(availWidth < this.minTabWidth){
35093 /*if(!this.sleft){ // incomplete scrolling code
35094 this.createScrollButtons();
35097 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35100 if(this.currentTabWidth < this.preferredTabWidth){
35101 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35107 * Returns the number of tabs in this TabPanel.
35110 getCount : function(){
35111 return this.items.length;
35115 * Resizes all the tabs to the passed width
35116 * @param {Number} The new width
35118 setTabWidth : function(width){
35119 this.currentTabWidth = width;
35120 for(var i = 0, len = this.items.length; i < len; i++) {
35121 if(!this.items[i].isHidden()) {
35122 this.items[i].setWidth(width);
35128 * Destroys this TabPanel
35129 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
35131 destroy : function(removeEl){
35132 Roo.EventManager.removeResizeListener(this.onResize, this);
35133 for(var i = 0, len = this.items.length; i < len; i++){
35134 this.items[i].purgeListeners();
35136 if(removeEl === true){
35137 this.el.update("");
35142 createStrip : function(container)
35144 var strip = document.createElement("nav");
35145 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
35146 container.appendChild(strip);
35150 createStripList : function(strip)
35152 // div wrapper for retard IE
35153 // returns the "tr" element.
35154 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
35155 //'<div class="x-tabs-strip-wrap">'+
35156 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
35157 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
35158 return strip.firstChild; //.firstChild.firstChild.firstChild;
35160 createBody : function(container)
35162 var body = document.createElement("div");
35163 Roo.id(body, "tab-body");
35164 //Roo.fly(body).addClass("x-tabs-body");
35165 Roo.fly(body).addClass("tab-content");
35166 container.appendChild(body);
35169 createItemBody :function(bodyEl, id){
35170 var body = Roo.getDom(id);
35172 body = document.createElement("div");
35175 //Roo.fly(body).addClass("x-tabs-item-body");
35176 Roo.fly(body).addClass("tab-pane");
35177 bodyEl.insertBefore(body, bodyEl.firstChild);
35181 createStripElements : function(stripEl, text, closable)
35183 var td = document.createElement("li"); // was td..
35186 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
35189 stripEl.appendChild(td);
35191 td.className = "x-tabs-closable";
35192 if(!this.closeTpl){
35193 this.closeTpl = new Roo.Template(
35194 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35195 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
35196 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
35199 var el = this.closeTpl.overwrite(td, {"text": text});
35200 var close = el.getElementsByTagName("div")[0];
35201 var inner = el.getElementsByTagName("em")[0];
35202 return {"el": el, "close": close, "inner": inner};
35205 // not sure what this is..
35207 //this.tabTpl = new Roo.Template(
35208 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35209 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
35211 this.tabTpl = new Roo.Template(
35213 '<span unselectable="on"' +
35214 (this.disableTooltips ? '' : ' title="{text}"') +
35215 ' >{text}</span></span></a>'
35219 var el = this.tabTpl.overwrite(td, {"text": text});
35220 var inner = el.getElementsByTagName("span")[0];
35221 return {"el": el, "inner": inner};
35229 * @class Roo.TabPanelItem
35230 * @extends Roo.util.Observable
35231 * Represents an individual item (tab plus body) in a TabPanel.
35232 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35233 * @param {String} id The id of this TabPanelItem
35234 * @param {String} text The text for the tab of this TabPanelItem
35235 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35237 Roo.bootstrap.panel.TabItem = function(config){
35239 * The {@link Roo.TabPanel} this TabPanelItem belongs to
35240 * @type Roo.TabPanel
35242 this.tabPanel = config.panel;
35244 * The id for this TabPanelItem
35247 this.id = config.id;
35249 this.disabled = false;
35251 this.text = config.text;
35253 this.loaded = false;
35254 this.closable = config.closable;
35257 * The body element for this TabPanelItem.
35258 * @type Roo.Element
35260 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35261 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35262 this.bodyEl.setStyle("display", "block");
35263 this.bodyEl.setStyle("zoom", "1");
35264 //this.hideAction();
35266 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
35268 this.el = Roo.get(els.el);
35269 this.inner = Roo.get(els.inner, true);
35270 this.textEl = Roo.get(this.el.dom.firstChild, true);
35271 this.pnode = Roo.get(els.el.parentNode, true);
35272 this.el.on("mousedown", this.onTabMouseDown, this);
35273 this.el.on("click", this.onTabClick, this);
35275 if(config.closable){
35276 var c = Roo.get(els.close, true);
35277 c.dom.title = this.closeText;
35278 c.addClassOnOver("close-over");
35279 c.on("click", this.closeClick, this);
35285 * Fires when this tab becomes the active tab.
35286 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35287 * @param {Roo.TabPanelItem} this
35291 * @event beforeclose
35292 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35293 * @param {Roo.TabPanelItem} this
35294 * @param {Object} e Set cancel to true on this object to cancel the close.
35296 "beforeclose": true,
35299 * Fires when this tab is closed.
35300 * @param {Roo.TabPanelItem} this
35304 * @event deactivate
35305 * Fires when this tab is no longer the active tab.
35306 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35307 * @param {Roo.TabPanelItem} this
35309 "deactivate" : true
35311 this.hidden = false;
35313 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35316 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35318 purgeListeners : function(){
35319 Roo.util.Observable.prototype.purgeListeners.call(this);
35320 this.el.removeAllListeners();
35323 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35326 this.pnode.addClass("active");
35329 this.tabPanel.stripWrap.repaint();
35331 this.fireEvent("activate", this.tabPanel, this);
35335 * Returns true if this tab is the active tab.
35336 * @return {Boolean}
35338 isActive : function(){
35339 return this.tabPanel.getActiveTab() == this;
35343 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35346 this.pnode.removeClass("active");
35348 this.fireEvent("deactivate", this.tabPanel, this);
35351 hideAction : function(){
35352 this.bodyEl.hide();
35353 this.bodyEl.setStyle("position", "absolute");
35354 this.bodyEl.setLeft("-20000px");
35355 this.bodyEl.setTop("-20000px");
35358 showAction : function(){
35359 this.bodyEl.setStyle("position", "relative");
35360 this.bodyEl.setTop("");
35361 this.bodyEl.setLeft("");
35362 this.bodyEl.show();
35366 * Set the tooltip for the tab.
35367 * @param {String} tooltip The tab's tooltip
35369 setTooltip : function(text){
35370 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35371 this.textEl.dom.qtip = text;
35372 this.textEl.dom.removeAttribute('title');
35374 this.textEl.dom.title = text;
35378 onTabClick : function(e){
35379 e.preventDefault();
35380 this.tabPanel.activate(this.id);
35383 onTabMouseDown : function(e){
35384 e.preventDefault();
35385 this.tabPanel.activate(this.id);
35388 getWidth : function(){
35389 return this.inner.getWidth();
35392 setWidth : function(width){
35393 var iwidth = width - this.pnode.getPadding("lr");
35394 this.inner.setWidth(iwidth);
35395 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35396 this.pnode.setWidth(width);
35400 * Show or hide the tab
35401 * @param {Boolean} hidden True to hide or false to show.
35403 setHidden : function(hidden){
35404 this.hidden = hidden;
35405 this.pnode.setStyle("display", hidden ? "none" : "");
35409 * Returns true if this tab is "hidden"
35410 * @return {Boolean}
35412 isHidden : function(){
35413 return this.hidden;
35417 * Returns the text for this tab
35420 getText : function(){
35424 autoSize : function(){
35425 //this.el.beginMeasure();
35426 this.textEl.setWidth(1);
35428 * #2804 [new] Tabs in Roojs
35429 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35431 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35432 //this.el.endMeasure();
35436 * Sets the text for the tab (Note: this also sets the tooltip text)
35437 * @param {String} text The tab's text and tooltip
35439 setText : function(text){
35441 this.textEl.update(text);
35442 this.setTooltip(text);
35443 //if(!this.tabPanel.resizeTabs){
35444 // this.autoSize();
35448 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35450 activate : function(){
35451 this.tabPanel.activate(this.id);
35455 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35457 disable : function(){
35458 if(this.tabPanel.active != this){
35459 this.disabled = true;
35460 this.pnode.addClass("disabled");
35465 * Enables this TabPanelItem if it was previously disabled.
35467 enable : function(){
35468 this.disabled = false;
35469 this.pnode.removeClass("disabled");
35473 * Sets the content for this TabPanelItem.
35474 * @param {String} content The content
35475 * @param {Boolean} loadScripts true to look for and load scripts
35477 setContent : function(content, loadScripts){
35478 this.bodyEl.update(content, loadScripts);
35482 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35483 * @return {Roo.UpdateManager} The UpdateManager
35485 getUpdateManager : function(){
35486 return this.bodyEl.getUpdateManager();
35490 * Set a URL to be used to load the content for this TabPanelItem.
35491 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35492 * @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)
35493 * @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)
35494 * @return {Roo.UpdateManager} The UpdateManager
35496 setUrl : function(url, params, loadOnce){
35497 if(this.refreshDelegate){
35498 this.un('activate', this.refreshDelegate);
35500 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35501 this.on("activate", this.refreshDelegate);
35502 return this.bodyEl.getUpdateManager();
35506 _handleRefresh : function(url, params, loadOnce){
35507 if(!loadOnce || !this.loaded){
35508 var updater = this.bodyEl.getUpdateManager();
35509 updater.update(url, params, this._setLoaded.createDelegate(this));
35514 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
35515 * Will fail silently if the setUrl method has not been called.
35516 * This does not activate the panel, just updates its content.
35518 refresh : function(){
35519 if(this.refreshDelegate){
35520 this.loaded = false;
35521 this.refreshDelegate();
35526 _setLoaded : function(){
35527 this.loaded = true;
35531 closeClick : function(e){
35534 this.fireEvent("beforeclose", this, o);
35535 if(o.cancel !== true){
35536 this.tabPanel.removeTab(this.id);
35540 * The text displayed in the tooltip for the close icon.
35543 closeText : "Close this tab"