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){
395 config = config || {};
397 Roo.bootstrap.Body.superclass.constructor.call(this, config);
398 this.el = Roo.get(config.el ? config.el : document.body );
399 if (this.cls && this.cls.length) {
400 Roo.get(document.body).addClass(this.cls);
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
406 is_body : true,// just to make sure it's constructed?
411 onRender : function(ct, position)
413 /* Roo.log("Roo.bootstrap.Body - onRender");
414 if (this.cls && this.cls.length) {
415 Roo.get(document.body).addClass(this.cls);
434 * @class Roo.bootstrap.ButtonGroup
435 * @extends Roo.bootstrap.Component
436 * Bootstrap ButtonGroup class
437 * @cfg {String} size lg | sm | xs (default empty normal)
438 * @cfg {String} align vertical | justified (default none)
439 * @cfg {String} direction up | down (default down)
440 * @cfg {Boolean} toolbar false | true
441 * @cfg {Boolean} btn true | false
446 * @param {Object} config The config object
449 Roo.bootstrap.ButtonGroup = function(config){
450 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
461 getAutoCreate : function(){
467 cfg.html = this.html || cfg.html;
478 if (['vertical','justified'].indexOf(this.align)!==-1) {
479 cfg.cls = 'btn-group-' + this.align;
481 if (this.align == 'justified') {
482 console.log(this.items);
486 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487 cfg.cls += ' btn-group-' + this.size;
490 if (this.direction == 'up') {
491 cfg.cls += ' dropup' ;
507 * @class Roo.bootstrap.Button
508 * @extends Roo.bootstrap.Component
509 * Bootstrap Button class
510 * @cfg {String} html The button content
511 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
512 * @cfg {String} size ( lg | sm | xs)
513 * @cfg {String} tag ( a | input | submit)
514 * @cfg {String} href empty or href
515 * @cfg {Boolean} disabled default false;
516 * @cfg {Boolean} isClose default false;
517 * @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)
518 * @cfg {String} badge text for badge
519 * @cfg {String} theme default
520 * @cfg {Boolean} inverse
521 * @cfg {Boolean} toggle
522 * @cfg {String} ontext text for on toggle state
523 * @cfg {String} offtext text for off toggle state
524 * @cfg {Boolean} defaulton
525 * @cfg {Boolean} preventDefault default true
526 * @cfg {Boolean} removeClass remove the standard class..
527 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
530 * Create a new button
531 * @param {Object} config The config object
535 Roo.bootstrap.Button = function(config){
536 Roo.bootstrap.Button.superclass.constructor.call(this, config);
541 * When a butotn is pressed
542 * @param {Roo.bootstrap.Button} this
543 * @param {Roo.EventObject} e
548 * After the button has been toggles
549 * @param {Roo.EventObject} e
550 * @param {boolean} pressed (also available as button.pressed)
556 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
574 preventDefault: true,
583 getAutoCreate : function(){
591 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
592 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
597 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
599 if (this.toggle == true) {
602 cls: 'slider-frame roo-button',
607 'data-off-text':'OFF',
608 cls: 'slider-button',
614 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
615 cfg.cls += ' '+this.weight;
624 cfg["aria-hidden"] = true;
626 cfg.html = "×";
632 if (this.theme==='default') {
633 cfg.cls = 'btn roo-button';
635 //if (this.parentType != 'Navbar') {
636 this.weight = this.weight.length ? this.weight : 'default';
638 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
640 cfg.cls += ' btn-' + this.weight;
642 } else if (this.theme==='glow') {
645 cfg.cls = 'btn-glow roo-button';
647 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
649 cfg.cls += ' ' + this.weight;
655 this.cls += ' inverse';
660 cfg.cls += ' active';
664 cfg.disabled = 'disabled';
668 Roo.log('changing to ul' );
670 this.glyphicon = 'caret';
673 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
675 //gsRoo.log(this.parentType);
676 if (this.parentType === 'Navbar' && !this.parent().bar) {
677 Roo.log('changing to li?');
686 href : this.href || '#'
689 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
690 cfg.cls += ' dropdown';
697 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
699 if (this.glyphicon) {
700 cfg.html = ' ' + cfg.html;
705 cls: 'glyphicon glyphicon-' + this.glyphicon
715 // cfg.cls='btn roo-button';
719 var value = cfg.html;
724 cls: 'glyphicon glyphicon-' + this.glyphicon,
743 cfg.cls += ' dropdown';
744 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
747 if (cfg.tag !== 'a' && this.href !== '') {
748 throw "Tag must be a to set href.";
749 } else if (this.href.length > 0) {
750 cfg.href = this.href;
753 if(this.removeClass){
758 cfg.target = this.target;
763 initEvents: function() {
764 // Roo.log('init events?');
765 // Roo.log(this.el.dom);
768 if (typeof (this.menu) != 'undefined') {
769 this.menu.parentType = this.xtype;
770 this.menu.triggerEl = this.el;
771 this.addxtype(Roo.apply({}, this.menu));
775 if (this.el.hasClass('roo-button')) {
776 this.el.on('click', this.onClick, this);
778 this.el.select('.roo-button').on('click', this.onClick, this);
781 if(this.removeClass){
782 this.el.on('click', this.onClick, this);
785 this.el.enableDisplayMode();
788 onClick : function(e)
795 Roo.log('button on click ');
796 if(this.preventDefault){
799 if (this.pressed === true || this.pressed === false) {
800 this.pressed = !this.pressed;
801 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
802 this.fireEvent('toggle', this, e, this.pressed);
806 this.fireEvent('click', this, e);
810 * Enables this button
814 this.disabled = false;
815 this.el.removeClass('disabled');
819 * Disable this button
823 this.disabled = true;
824 this.el.addClass('disabled');
827 * sets the active state on/off,
828 * @param {Boolean} state (optional) Force a particular state
830 setActive : function(v) {
832 this.el[v ? 'addClass' : 'removeClass']('active');
835 * toggles the current active state
837 toggleActive : function()
839 var active = this.el.hasClass('active');
840 this.setActive(!active);
844 setText : function(str)
846 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
850 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
873 * @class Roo.bootstrap.Column
874 * @extends Roo.bootstrap.Component
875 * Bootstrap Column class
876 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
877 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
878 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
879 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
880 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
881 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
882 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
883 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
886 * @cfg {Boolean} hidden (true|false) hide the element
887 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
888 * @cfg {String} fa (ban|check|...) font awesome icon
889 * @cfg {Number} fasize (1|2|....) font awsome size
891 * @cfg {String} icon (info-sign|check|...) glyphicon name
893 * @cfg {String} html content of column.
896 * Create a new Column
897 * @param {Object} config The config object
900 Roo.bootstrap.Column = function(config){
901 Roo.bootstrap.Column.superclass.constructor.call(this, config);
904 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
922 getAutoCreate : function(){
923 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
931 ['xs','sm','md','lg'].map(function(size){
932 //Roo.log( size + ':' + settings[size]);
934 if (settings[size+'off'] !== false) {
935 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
938 if (settings[size] === false) {
942 if (!settings[size]) { // 0 = hidden
943 cfg.cls += ' hidden-' + size;
946 cfg.cls += ' col-' + size + '-' + settings[size];
951 cfg.cls += ' hidden';
954 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
955 cfg.cls +=' alert alert-' + this.alert;
959 if (this.html.length) {
960 cfg.html = this.html;
964 if (this.fasize > 1) {
965 fasize = ' fa-' + this.fasize + 'x';
967 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
972 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
991 * @class Roo.bootstrap.Container
992 * @extends Roo.bootstrap.Component
993 * Bootstrap Container class
994 * @cfg {Boolean} jumbotron is it a jumbotron element
995 * @cfg {String} html content of element
996 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
997 * @cfg {String} panel (primary|success|info|warning|danger) render as panel - type - primary/success.....
998 * @cfg {String} header content of header (for panel)
999 * @cfg {String} footer content of footer (for panel)
1000 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1001 * @cfg {String} tag (header|aside|section) type of HTML tag.
1002 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1003 * @cfg {String} fa font awesome icon
1004 * @cfg {String} icon (info-sign|check|...) glyphicon name
1005 * @cfg {Boolean} hidden (true|false) hide the element
1006 * @cfg {Boolean} expandable (true|false) default false
1007 * @cfg {Boolean} expanded (true|false) default true
1008 * @cfg {String} rheader contet on the right of header
1009 * @cfg {Boolean} clickable (true|false) default false
1013 * Create a new Container
1014 * @param {Object} config The config object
1017 Roo.bootstrap.Container = function(config){
1018 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1024 * After the panel has been expand
1026 * @param {Roo.bootstrap.Container} this
1031 * After the panel has been collapsed
1033 * @param {Roo.bootstrap.Container} this
1038 * When a element is chick
1039 * @param {Roo.bootstrap.Container} this
1040 * @param {Roo.EventObject} e
1046 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1064 getChildContainer : function() {
1070 if (this.panel.length) {
1071 return this.el.select('.panel-body',true).first();
1078 getAutoCreate : function(){
1081 tag : this.tag || 'div',
1085 if (this.jumbotron) {
1086 cfg.cls = 'jumbotron';
1091 // - this is applied by the parent..
1093 // cfg.cls = this.cls + '';
1096 if (this.sticky.length) {
1098 var bd = Roo.get(document.body);
1099 if (!bd.hasClass('bootstrap-sticky')) {
1100 bd.addClass('bootstrap-sticky');
1101 Roo.select('html',true).setStyle('height', '100%');
1104 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1108 if (this.well.length) {
1109 switch (this.well) {
1112 cfg.cls +=' well well-' +this.well;
1121 cfg.cls += ' hidden';
1125 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1126 cfg.cls +=' alert alert-' + this.alert;
1131 if (this.panel.length) {
1132 cfg.cls += ' panel panel-' + this.panel;
1134 if (this.header.length) {
1138 if(this.expandable){
1140 cfg.cls = cfg.cls + ' expandable';
1144 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1152 cls : 'panel-title',
1153 html : (this.expandable ? ' ' : '') + this.header
1157 cls: 'panel-header-right',
1163 cls : 'panel-heading',
1164 style : this.expandable ? 'cursor: pointer' : '',
1172 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1177 if (this.footer.length) {
1179 cls : 'panel-footer',
1188 body.html = this.html || cfg.html;
1189 // prefix with the icons..
1191 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1194 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1199 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1200 cfg.cls = 'container';
1206 initEvents: function()
1208 if(this.expandable){
1209 var headerEl = this.headerEl();
1212 headerEl.on('click', this.onToggleClick, this);
1217 this.el.on('click', this.onClick, this);
1222 onToggleClick : function()
1224 var headerEl = this.headerEl();
1240 if(this.fireEvent('expand', this)) {
1242 this.expanded = true;
1244 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1246 this.el.select('.panel-body',true).first().removeClass('hide');
1248 var toggleEl = this.toggleEl();
1254 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1259 collapse : function()
1261 if(this.fireEvent('collapse', this)) {
1263 this.expanded = false;
1265 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1266 this.el.select('.panel-body',true).first().addClass('hide');
1268 var toggleEl = this.toggleEl();
1274 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1278 toggleEl : function()
1280 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1284 return this.el.select('.panel-heading .fa',true).first();
1287 headerEl : function()
1289 if(!this.el || !this.panel.length || !this.header.length){
1293 return this.el.select('.panel-heading',true).first()
1296 titleEl : function()
1298 if(!this.el || !this.panel.length || !this.header.length){
1302 return this.el.select('.panel-title',true).first();
1305 setTitle : function(v)
1307 var titleEl = this.titleEl();
1313 titleEl.dom.innerHTML = v;
1316 getTitle : function()
1319 var titleEl = this.titleEl();
1325 return titleEl.dom.innerHTML;
1328 setRightTitle : function(v)
1330 var t = this.el.select('.panel-header-right',true).first();
1336 t.dom.innerHTML = v;
1339 onClick : function(e)
1343 this.fireEvent('click', this, e);
1357 * @class Roo.bootstrap.Img
1358 * @extends Roo.bootstrap.Component
1359 * Bootstrap Img class
1360 * @cfg {Boolean} imgResponsive false | true
1361 * @cfg {String} border rounded | circle | thumbnail
1362 * @cfg {String} src image source
1363 * @cfg {String} alt image alternative text
1364 * @cfg {String} href a tag href
1365 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1366 * @cfg {String} xsUrl xs image source
1367 * @cfg {String} smUrl sm image source
1368 * @cfg {String} mdUrl md image source
1369 * @cfg {String} lgUrl lg image source
1372 * Create a new Input
1373 * @param {Object} config The config object
1376 Roo.bootstrap.Img = function(config){
1377 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1383 * The img click event for the img.
1384 * @param {Roo.EventObject} e
1390 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1392 imgResponsive: true,
1402 getAutoCreate : function()
1404 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1405 return this.createSingleImg();
1410 cls: 'roo-image-responsive-group',
1415 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1417 if(!_this[size + 'Url']){
1423 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1424 html: _this.html || cfg.html,
1425 src: _this[size + 'Url']
1428 img.cls += ' roo-image-responsive-' + size;
1430 var s = ['xs', 'sm', 'md', 'lg'];
1432 s.splice(s.indexOf(size), 1);
1434 Roo.each(s, function(ss){
1435 img.cls += ' hidden-' + ss;
1438 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1439 cfg.cls += ' img-' + _this.border;
1443 cfg.alt = _this.alt;
1456 a.target = _this.target;
1460 cfg.cn.push((_this.href) ? a : img);
1467 createSingleImg : function()
1471 cls: (this.imgResponsive) ? 'img-responsive' : '',
1473 src : 'about:blank' // just incase src get's set to undefined?!?
1476 cfg.html = this.html || cfg.html;
1478 cfg.src = this.src || cfg.src;
1480 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1481 cfg.cls += ' img-' + this.border;
1498 a.target = this.target;
1503 return (this.href) ? a : cfg;
1506 initEvents: function()
1509 this.el.on('click', this.onClick, this);
1514 onClick : function(e)
1516 Roo.log('img onclick');
1517 this.fireEvent('click', this, e);
1520 * Sets the url of the image - used to update it
1521 * @param {String} url the url of the image
1524 setSrc : function(url)
1528 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1529 this.el.dom.src = url;
1533 this.el.select('img', true).first().dom.src = url;
1549 * @class Roo.bootstrap.Link
1550 * @extends Roo.bootstrap.Component
1551 * Bootstrap Link Class
1552 * @cfg {String} alt image alternative text
1553 * @cfg {String} href a tag href
1554 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1555 * @cfg {String} html the content of the link.
1556 * @cfg {String} anchor name for the anchor link
1557 * @cfg {String} fa - favicon
1559 * @cfg {Boolean} preventDefault (true | false) default false
1563 * Create a new Input
1564 * @param {Object} config The config object
1567 Roo.bootstrap.Link = function(config){
1568 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1574 * The img click event for the img.
1575 * @param {Roo.EventObject} e
1581 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1585 preventDefault: false,
1591 getAutoCreate : function()
1593 var html = this.html || '';
1595 if (this.fa !== false) {
1596 html = '<i class="fa fa-' + this.fa + '"></i>';
1601 // anchor's do not require html/href...
1602 if (this.anchor === false) {
1604 cfg.href = this.href || '#';
1606 cfg.name = this.anchor;
1607 if (this.html !== false || this.fa !== false) {
1610 if (this.href !== false) {
1611 cfg.href = this.href;
1615 if(this.alt !== false){
1620 if(this.target !== false) {
1621 cfg.target = this.target;
1627 initEvents: function() {
1629 if(!this.href || this.preventDefault){
1630 this.el.on('click', this.onClick, this);
1634 onClick : function(e)
1636 if(this.preventDefault){
1639 //Roo.log('img onclick');
1640 this.fireEvent('click', this, e);
1653 * @class Roo.bootstrap.Header
1654 * @extends Roo.bootstrap.Component
1655 * Bootstrap Header class
1656 * @cfg {String} html content of header
1657 * @cfg {Number} level (1|2|3|4|5|6) default 1
1660 * Create a new Header
1661 * @param {Object} config The config object
1665 Roo.bootstrap.Header = function(config){
1666 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1669 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1677 getAutoCreate : function(){
1682 tag: 'h' + (1 *this.level),
1683 html: this.html || ''
1695 * Ext JS Library 1.1.1
1696 * Copyright(c) 2006-2007, Ext JS, LLC.
1698 * Originally Released Under LGPL - original licence link has changed is not relivant.
1701 * <script type="text/javascript">
1705 * @class Roo.bootstrap.MenuMgr
1706 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1709 Roo.bootstrap.MenuMgr = function(){
1710 var menus, active, groups = {}, attached = false, lastShow = new Date();
1712 // private - called when first menu is created
1715 active = new Roo.util.MixedCollection();
1716 Roo.get(document).addKeyListener(27, function(){
1717 if(active.length > 0){
1725 if(active && active.length > 0){
1726 var c = active.clone();
1736 if(active.length < 1){
1737 Roo.get(document).un("mouseup", onMouseDown);
1745 var last = active.last();
1746 lastShow = new Date();
1749 Roo.get(document).on("mouseup", onMouseDown);
1754 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1755 m.parentMenu.activeChild = m;
1756 }else if(last && last.isVisible()){
1757 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1762 function onBeforeHide(m){
1764 m.activeChild.hide();
1766 if(m.autoHideTimer){
1767 clearTimeout(m.autoHideTimer);
1768 delete m.autoHideTimer;
1773 function onBeforeShow(m){
1774 var pm = m.parentMenu;
1775 if(!pm && !m.allowOtherMenus){
1777 }else if(pm && pm.activeChild && active != m){
1778 pm.activeChild.hide();
1782 // private this should really trigger on mouseup..
1783 function onMouseDown(e){
1784 Roo.log("on Mouse Up");
1786 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1787 Roo.log("MenuManager hideAll");
1796 function onBeforeCheck(mi, state){
1798 var g = groups[mi.group];
1799 for(var i = 0, l = g.length; i < l; i++){
1801 g[i].setChecked(false);
1810 * Hides all menus that are currently visible
1812 hideAll : function(){
1817 register : function(menu){
1821 menus[menu.id] = menu;
1822 menu.on("beforehide", onBeforeHide);
1823 menu.on("hide", onHide);
1824 menu.on("beforeshow", onBeforeShow);
1825 menu.on("show", onShow);
1827 if(g && menu.events["checkchange"]){
1831 groups[g].push(menu);
1832 menu.on("checkchange", onCheck);
1837 * Returns a {@link Roo.menu.Menu} object
1838 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1839 * be used to generate and return a new Menu instance.
1841 get : function(menu){
1842 if(typeof menu == "string"){ // menu id
1844 }else if(menu.events){ // menu instance
1847 /*else if(typeof menu.length == 'number'){ // array of menu items?
1848 return new Roo.bootstrap.Menu({items:menu});
1849 }else{ // otherwise, must be a config
1850 return new Roo.bootstrap.Menu(menu);
1857 unregister : function(menu){
1858 delete menus[menu.id];
1859 menu.un("beforehide", onBeforeHide);
1860 menu.un("hide", onHide);
1861 menu.un("beforeshow", onBeforeShow);
1862 menu.un("show", onShow);
1864 if(g && menu.events["checkchange"]){
1865 groups[g].remove(menu);
1866 menu.un("checkchange", onCheck);
1871 registerCheckable : function(menuItem){
1872 var g = menuItem.group;
1877 groups[g].push(menuItem);
1878 menuItem.on("beforecheckchange", onBeforeCheck);
1883 unregisterCheckable : function(menuItem){
1884 var g = menuItem.group;
1886 groups[g].remove(menuItem);
1887 menuItem.un("beforecheckchange", onBeforeCheck);
1899 * @class Roo.bootstrap.Menu
1900 * @extends Roo.bootstrap.Component
1901 * Bootstrap Menu class - container for MenuItems
1902 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1903 * @cfg {bool} hidden if the menu should be hidden when rendered.
1904 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1905 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1909 * @param {Object} config The config object
1913 Roo.bootstrap.Menu = function(config){
1914 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1915 if (this.registerMenu && this.type != 'treeview') {
1916 Roo.bootstrap.MenuMgr.register(this);
1921 * Fires before this menu is displayed
1922 * @param {Roo.menu.Menu} this
1927 * Fires before this menu is hidden
1928 * @param {Roo.menu.Menu} this
1933 * Fires after this menu is displayed
1934 * @param {Roo.menu.Menu} this
1939 * Fires after this menu is hidden
1940 * @param {Roo.menu.Menu} this
1945 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1946 * @param {Roo.menu.Menu} this
1947 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1948 * @param {Roo.EventObject} e
1953 * Fires when the mouse is hovering over 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 the mouse exits this menu
1962 * @param {Roo.menu.Menu} this
1963 * @param {Roo.EventObject} e
1964 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1969 * Fires when a menu item contained in this menu is clicked
1970 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1971 * @param {Roo.EventObject} e
1975 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1978 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1982 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1985 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1987 registerMenu : true,
1989 menuItems :false, // stores the menu items..
1999 getChildContainer : function() {
2003 getAutoCreate : function(){
2005 //if (['right'].indexOf(this.align)!==-1) {
2006 // cfg.cn[1].cls += ' pull-right'
2012 cls : 'dropdown-menu' ,
2013 style : 'z-index:1000'
2017 if (this.type === 'submenu') {
2018 cfg.cls = 'submenu active';
2020 if (this.type === 'treeview') {
2021 cfg.cls = 'treeview-menu';
2026 initEvents : function() {
2028 // Roo.log("ADD event");
2029 // Roo.log(this.triggerEl.dom);
2031 this.triggerEl.on('click', this.onTriggerClick, this);
2033 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2035 this.triggerEl.addClass('dropdown-toggle');
2038 this.el.on('touchstart' , this.onTouch, this);
2040 this.el.on('click' , this.onClick, this);
2042 this.el.on("mouseover", this.onMouseOver, this);
2043 this.el.on("mouseout", this.onMouseOut, this);
2047 findTargetItem : function(e)
2049 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2053 //Roo.log(t); Roo.log(t.id);
2055 //Roo.log(this.menuitems);
2056 return this.menuitems.get(t.id);
2058 //return this.items.get(t.menuItemId);
2064 onTouch : function(e)
2066 Roo.log("menu.onTouch");
2067 //e.stopEvent(); this make the user popdown broken
2071 onClick : function(e)
2073 Roo.log("menu.onClick");
2075 var t = this.findTargetItem(e);
2076 if(!t || t.isContainer){
2081 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2082 if(t == this.activeItem && t.shouldDeactivate(e)){
2083 this.activeItem.deactivate();
2084 delete this.activeItem;
2088 this.setActiveItem(t, true);
2096 Roo.log('pass click event');
2100 this.fireEvent("click", this, t, e);
2104 (function() { _this.hide(); }).defer(100);
2107 onMouseOver : function(e){
2108 var t = this.findTargetItem(e);
2111 // if(t.canActivate && !t.disabled){
2112 // this.setActiveItem(t, true);
2116 this.fireEvent("mouseover", this, e, t);
2118 isVisible : function(){
2119 return !this.hidden;
2121 onMouseOut : function(e){
2122 var t = this.findTargetItem(e);
2125 // if(t == this.activeItem && t.shouldDeactivate(e)){
2126 // this.activeItem.deactivate();
2127 // delete this.activeItem;
2130 this.fireEvent("mouseout", this, e, t);
2135 * Displays this menu relative to another element
2136 * @param {String/HTMLElement/Roo.Element} element The element to align to
2137 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2138 * the element (defaults to this.defaultAlign)
2139 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2141 show : function(el, pos, parentMenu){
2142 this.parentMenu = parentMenu;
2146 this.fireEvent("beforeshow", this);
2147 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2150 * Displays this menu at a specific xy position
2151 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2152 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2154 showAt : function(xy, parentMenu, /* private: */_e){
2155 this.parentMenu = parentMenu;
2160 this.fireEvent("beforeshow", this);
2161 //xy = this.el.adjustForConstraints(xy);
2165 this.hideMenuItems();
2166 this.hidden = false;
2167 this.triggerEl.addClass('open');
2169 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2170 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2173 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2178 this.fireEvent("show", this);
2184 this.doFocus.defer(50, this);
2188 doFocus : function(){
2190 this.focusEl.focus();
2195 * Hides this menu and optionally all parent menus
2196 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2198 hide : function(deep)
2201 this.hideMenuItems();
2202 if(this.el && this.isVisible()){
2203 this.fireEvent("beforehide", this);
2204 if(this.activeItem){
2205 this.activeItem.deactivate();
2206 this.activeItem = null;
2208 this.triggerEl.removeClass('open');;
2210 this.fireEvent("hide", this);
2212 if(deep === true && this.parentMenu){
2213 this.parentMenu.hide(true);
2217 onTriggerClick : function(e)
2219 Roo.log('trigger click');
2221 var target = e.getTarget();
2223 Roo.log(target.nodeName.toLowerCase());
2225 if(target.nodeName.toLowerCase() === 'i'){
2231 onTriggerPress : function(e)
2233 Roo.log('trigger press');
2234 //Roo.log(e.getTarget());
2235 // Roo.log(this.triggerEl.dom);
2237 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2238 var pel = Roo.get(e.getTarget());
2239 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2240 Roo.log('is treeview or dropdown?');
2244 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2248 if (this.isVisible()) {
2253 this.show(this.triggerEl, false, false);
2256 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2263 hideMenuItems : function()
2265 Roo.log("hide Menu Items");
2269 //$(backdrop).remove()
2270 this.el.select('.open',true).each(function(aa) {
2272 aa.removeClass('open');
2273 //var parent = getParent($(this))
2274 //var relatedTarget = { relatedTarget: this }
2276 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2277 //if (e.isDefaultPrevented()) return
2278 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2281 addxtypeChild : function (tree, cntr) {
2282 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2284 this.menuitems.add(comp);
2305 * @class Roo.bootstrap.MenuItem
2306 * @extends Roo.bootstrap.Component
2307 * Bootstrap MenuItem class
2308 * @cfg {String} html the menu label
2309 * @cfg {String} href the link
2310 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2311 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2312 * @cfg {Boolean} active used on sidebars to highlight active itesm
2313 * @cfg {String} fa favicon to show on left of menu item.
2314 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2318 * Create a new MenuItem
2319 * @param {Object} config The config object
2323 Roo.bootstrap.MenuItem = function(config){
2324 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2329 * The raw click event for the entire grid.
2330 * @param {Roo.bootstrap.MenuItem} this
2331 * @param {Roo.EventObject} e
2337 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2341 preventDefault: false,
2342 isContainer : false,
2346 getAutoCreate : function(){
2348 if(this.isContainer){
2351 cls: 'dropdown-menu-item'
2365 if (this.fa !== false) {
2368 cls : 'fa fa-' + this.fa
2377 cls: 'dropdown-menu-item',
2380 if (this.parent().type == 'treeview') {
2381 cfg.cls = 'treeview-menu';
2384 cfg.cls += ' active';
2389 anc.href = this.href || cfg.cn[0].href ;
2390 ctag.html = this.html || cfg.cn[0].html ;
2394 initEvents: function()
2396 if (this.parent().type == 'treeview') {
2397 this.el.select('a').on('click', this.onClick, this);
2400 this.menu.parentType = this.xtype;
2401 this.menu.triggerEl = this.el;
2402 this.menu = this.addxtype(Roo.apply({}, this.menu));
2406 onClick : function(e)
2408 Roo.log('item on click ');
2410 if(this.preventDefault){
2413 //this.parent().hideMenuItems();
2415 this.fireEvent('click', this, e);
2434 * @class Roo.bootstrap.MenuSeparator
2435 * @extends Roo.bootstrap.Component
2436 * Bootstrap MenuSeparator class
2439 * Create a new MenuItem
2440 * @param {Object} config The config object
2444 Roo.bootstrap.MenuSeparator = function(config){
2445 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2448 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2450 getAutoCreate : function(){
2469 * @class Roo.bootstrap.Modal
2470 * @extends Roo.bootstrap.Component
2471 * Bootstrap Modal class
2472 * @cfg {String} title Title of dialog
2473 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2474 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2475 * @cfg {Boolean} specificTitle default false
2476 * @cfg {Array} buttons Array of buttons or standard button set..
2477 * @cfg {String} buttonPosition (left|right|center) default right
2478 * @cfg {Boolean} animate default true
2479 * @cfg {Boolean} allow_close default true
2480 * @cfg {Boolean} fitwindow default false
2481 * @cfg {String} size (sm|lg) default empty
2485 * Create a new Modal Dialog
2486 * @param {Object} config The config object
2489 Roo.bootstrap.Modal = function(config){
2490 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2495 * The raw btnclick event for the button
2496 * @param {Roo.EventObject} e
2500 this.buttons = this.buttons || [];
2503 this.tmpl = Roo.factory(this.tmpl);
2508 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2510 title : 'test dialog',
2520 specificTitle: false,
2522 buttonPosition: 'right',
2541 onRender : function(ct, position)
2543 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2546 var cfg = Roo.apply({}, this.getAutoCreate());
2549 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2551 //if (!cfg.name.length) {
2555 cfg.cls += ' ' + this.cls;
2558 cfg.style = this.style;
2560 this.el = Roo.get(document.body).createChild(cfg, position);
2562 //var type = this.el.dom.type;
2565 if(this.tabIndex !== undefined){
2566 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2569 this.dialogEl = this.el.select('.modal-dialog',true).first();
2570 this.bodyEl = this.el.select('.modal-body',true).first();
2571 this.closeEl = this.el.select('.modal-header .close', true).first();
2572 this.footerEl = this.el.select('.modal-footer',true).first();
2573 this.titleEl = this.el.select('.modal-title',true).first();
2577 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2578 this.maskEl.enableDisplayMode("block");
2580 //this.el.addClass("x-dlg-modal");
2582 if (this.buttons.length) {
2583 Roo.each(this.buttons, function(bb) {
2584 var b = Roo.apply({}, bb);
2585 b.xns = b.xns || Roo.bootstrap;
2586 b.xtype = b.xtype || 'Button';
2587 if (typeof(b.listeners) == 'undefined') {
2588 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2591 var btn = Roo.factory(b);
2593 btn.render(this.el.select('.modal-footer div').first());
2597 // render the children.
2600 if(typeof(this.items) != 'undefined'){
2601 var items = this.items;
2604 for(var i =0;i < items.length;i++) {
2605 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2609 this.items = nitems;
2611 // where are these used - they used to be body/close/footer
2615 //this.el.addClass([this.fieldClass, this.cls]);
2619 getAutoCreate : function(){
2624 html : this.html || ''
2629 cls : 'modal-title',
2633 if(this.specificTitle){
2639 if (this.allow_close) {
2651 if(this.size.length){
2652 size = 'modal-' + this.size;
2657 style : 'display: none',
2660 cls: "modal-dialog " + size,
2663 cls : "modal-content",
2666 cls : 'modal-header',
2671 cls : 'modal-footer',
2675 cls: 'btn-' + this.buttonPosition
2692 modal.cls += ' fade';
2698 getChildContainer : function() {
2703 getButtonContainer : function() {
2704 return this.el.select('.modal-footer div',true).first();
2707 initEvents : function()
2709 if (this.allow_close) {
2710 this.closeEl.on('click', this.hide, this);
2712 Roo.EventManager.onWindowResize(this.resize, this, true);
2719 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2720 if (this.fitwindow) {
2721 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2722 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2727 setSize : function(w,h)
2737 if (!this.rendered) {
2741 this.el.setStyle('display', 'block');
2743 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2746 this.el.addClass('in');
2749 this.el.addClass('in');
2753 // not sure how we can show data in here..
2755 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2758 Roo.get(document.body).addClass("x-body-masked");
2759 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2761 this.el.setStyle('zIndex', '10001');
2763 this.fireEvent('show', this);
2768 this.items.forEach( function(e) {
2769 e.layout ? e.layout() : false;
2777 if(this.fireEvent("beforehide", this) !== false){
2779 Roo.get(document.body).removeClass("x-body-masked");
2780 this.el.removeClass('in');
2781 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2783 if(this.animate){ // why
2785 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2787 this.el.setStyle('display', 'none');
2789 this.fireEvent('hide', this);
2793 addButton : function(str, cb)
2797 var b = Roo.apply({}, { html : str } );
2798 b.xns = b.xns || Roo.bootstrap;
2799 b.xtype = b.xtype || 'Button';
2800 if (typeof(b.listeners) == 'undefined') {
2801 b.listeners = { click : cb.createDelegate(this) };
2804 var btn = Roo.factory(b);
2806 btn.render(this.el.select('.modal-footer div').first());
2812 setDefaultButton : function(btn)
2814 //this.el.select('.modal-footer').()
2818 resizeTo: function(w,h)
2822 this.dialogEl.setWidth(w);
2823 if (this.diff === false) {
2824 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2827 this.bodyEl.setHeight(h-this.diff);
2831 setContentSize : function(w, h)
2835 onButtonClick: function(btn,e)
2838 this.fireEvent('btnclick', btn.name, e);
2841 * Set the title of the Dialog
2842 * @param {String} str new Title
2844 setTitle: function(str) {
2845 this.titleEl.dom.innerHTML = str;
2848 * Set the body of the Dialog
2849 * @param {String} str new Title
2851 setBody: function(str) {
2852 this.bodyEl.dom.innerHTML = str;
2855 * Set the body of the Dialog using the template
2856 * @param {Obj} data - apply this data to the template and replace the body contents.
2858 applyBody: function(obj)
2861 Roo.log("Error - using apply Body without a template");
2864 this.tmpl.overwrite(this.bodyEl, obj);
2870 Roo.apply(Roo.bootstrap.Modal, {
2872 * Button config that displays a single OK button
2881 * Button config that displays Yes and No buttons
2897 * Button config that displays OK and Cancel buttons
2912 * Button config that displays Yes, No and Cancel buttons
2934 * messagebox - can be used as a replace
2938 * @class Roo.MessageBox
2939 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2943 Roo.Msg.alert('Status', 'Changes saved successfully.');
2945 // Prompt for user data:
2946 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2948 // process text value...
2952 // Show a dialog using config options:
2954 title:'Save Changes?',
2955 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2956 buttons: Roo.Msg.YESNOCANCEL,
2963 Roo.bootstrap.MessageBox = function(){
2964 var dlg, opt, mask, waitTimer;
2965 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2966 var buttons, activeTextEl, bwidth;
2970 var handleButton = function(button){
2972 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2976 var handleHide = function(){
2978 dlg.el.removeClass(opt.cls);
2981 // Roo.TaskMgr.stop(waitTimer);
2982 // waitTimer = null;
2987 var updateButtons = function(b){
2990 buttons["ok"].hide();
2991 buttons["cancel"].hide();
2992 buttons["yes"].hide();
2993 buttons["no"].hide();
2994 //dlg.footer.dom.style.display = 'none';
2997 dlg.footerEl.dom.style.display = '';
2998 for(var k in buttons){
2999 if(typeof buttons[k] != "function"){
3002 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3003 width += buttons[k].el.getWidth()+15;
3013 var handleEsc = function(d, k, e){
3014 if(opt && opt.closable !== false){
3024 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3025 * @return {Roo.BasicDialog} The BasicDialog element
3027 getDialog : function(){
3029 dlg = new Roo.bootstrap.Modal( {
3032 //constraintoviewport:false,
3034 //collapsible : false,
3039 //buttonAlign:"center",
3040 closeClick : function(){
3041 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3044 handleButton("cancel");
3049 dlg.on("hide", handleHide);
3051 //dlg.addKeyListener(27, handleEsc);
3053 this.buttons = buttons;
3054 var bt = this.buttonText;
3055 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3056 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3057 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3058 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3060 bodyEl = dlg.bodyEl.createChild({
3062 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3063 '<textarea class="roo-mb-textarea"></textarea>' +
3064 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3066 msgEl = bodyEl.dom.firstChild;
3067 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3068 textboxEl.enableDisplayMode();
3069 textboxEl.addKeyListener([10,13], function(){
3070 if(dlg.isVisible() && opt && opt.buttons){
3073 }else if(opt.buttons.yes){
3074 handleButton("yes");
3078 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3079 textareaEl.enableDisplayMode();
3080 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3081 progressEl.enableDisplayMode();
3083 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3084 //var pf = progressEl.dom.firstChild;
3086 //pp = Roo.get(pf.firstChild);
3087 //pp.setHeight(pf.offsetHeight);
3095 * Updates the message box body text
3096 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3097 * the XHTML-compliant non-breaking space character '&#160;')
3098 * @return {Roo.MessageBox} This message box
3100 updateText : function(text)
3102 if(!dlg.isVisible() && !opt.width){
3103 dlg.dialogEl.setWidth(this.maxWidth);
3104 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3106 msgEl.innerHTML = text || ' ';
3108 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3109 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3111 Math.min(opt.width || cw , this.maxWidth),
3112 Math.max(opt.minWidth || this.minWidth, bwidth)
3115 activeTextEl.setWidth(w);
3117 if(dlg.isVisible()){
3118 dlg.fixedcenter = false;
3120 // to big, make it scroll. = But as usual stupid IE does not support
3123 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3124 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3125 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3127 bodyEl.dom.style.height = '';
3128 bodyEl.dom.style.overflowY = '';
3131 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3133 bodyEl.dom.style.overflowX = '';
3136 dlg.setContentSize(w, bodyEl.getHeight());
3137 if(dlg.isVisible()){
3138 dlg.fixedcenter = true;
3144 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3145 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3146 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3147 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3148 * @return {Roo.MessageBox} This message box
3150 updateProgress : function(value, text){
3152 this.updateText(text);
3154 if (pp) { // weird bug on my firefox - for some reason this is not defined
3155 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3161 * Returns true if the message box is currently displayed
3162 * @return {Boolean} True if the message box is visible, else false
3164 isVisible : function(){
3165 return dlg && dlg.isVisible();
3169 * Hides the message box if it is displayed
3172 if(this.isVisible()){
3178 * Displays a new message box, or reinitializes an existing message box, based on the config options
3179 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3180 * The following config object properties are supported:
3182 Property Type Description
3183 ---------- --------------- ------------------------------------------------------------------------------------
3184 animEl String/Element An id or Element from which the message box should animate as it opens and
3185 closes (defaults to undefined)
3186 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3187 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3188 closable Boolean False to hide the top-right close button (defaults to true). Note that
3189 progress and wait dialogs will ignore this property and always hide the
3190 close button as they can only be closed programmatically.
3191 cls String A custom CSS class to apply to the message box element
3192 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3193 displayed (defaults to 75)
3194 fn Function A callback function to execute after closing the dialog. The arguments to the
3195 function will be btn (the name of the button that was clicked, if applicable,
3196 e.g. "ok"), and text (the value of the active text field, if applicable).
3197 Progress and wait dialogs will ignore this option since they do not respond to
3198 user actions and can only be closed programmatically, so any required function
3199 should be called by the same code after it closes the dialog.
3200 icon String A CSS class that provides a background image to be used as an icon for
3201 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3202 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3203 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3204 modal Boolean False to allow user interaction with the page while the message box is
3205 displayed (defaults to true)
3206 msg String A string that will replace the existing message box body text (defaults
3207 to the XHTML-compliant non-breaking space character ' ')
3208 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3209 progress Boolean True to display a progress bar (defaults to false)
3210 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3211 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3212 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3213 title String The title text
3214 value String The string value to set into the active textbox element if displayed
3215 wait Boolean True to display a progress bar (defaults to false)
3216 width Number The width of the dialog in pixels
3223 msg: 'Please enter your address:',
3225 buttons: Roo.MessageBox.OKCANCEL,
3228 animEl: 'addAddressBtn'
3231 * @param {Object} config Configuration options
3232 * @return {Roo.MessageBox} This message box
3234 show : function(options)
3237 // this causes nightmares if you show one dialog after another
3238 // especially on callbacks..
3240 if(this.isVisible()){
3243 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3244 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3245 Roo.log("New Dialog Message:" + options.msg )
3246 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3247 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3250 var d = this.getDialog();
3252 d.setTitle(opt.title || " ");
3253 d.closeEl.setDisplayed(opt.closable !== false);
3254 activeTextEl = textboxEl;
3255 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3260 textareaEl.setHeight(typeof opt.multiline == "number" ?
3261 opt.multiline : this.defaultTextHeight);
3262 activeTextEl = textareaEl;
3271 progressEl.setDisplayed(opt.progress === true);
3272 this.updateProgress(0);
3273 activeTextEl.dom.value = opt.value || "";
3275 dlg.setDefaultButton(activeTextEl);
3277 var bs = opt.buttons;
3281 }else if(bs && bs.yes){
3282 db = buttons["yes"];
3284 dlg.setDefaultButton(db);
3286 bwidth = updateButtons(opt.buttons);
3287 this.updateText(opt.msg);
3289 d.el.addClass(opt.cls);
3291 d.proxyDrag = opt.proxyDrag === true;
3292 d.modal = opt.modal !== false;
3293 d.mask = opt.modal !== false ? mask : false;
3295 // force it to the end of the z-index stack so it gets a cursor in FF
3296 document.body.appendChild(dlg.el.dom);
3297 d.animateTarget = null;
3298 d.show(options.animEl);
3304 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3305 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3306 * and closing the message box when the process is complete.
3307 * @param {String} title The title bar text
3308 * @param {String} msg The message box body text
3309 * @return {Roo.MessageBox} This message box
3311 progress : function(title, msg){
3318 minWidth: this.minProgressWidth,
3325 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3326 * If a callback function is passed it will be called after the user clicks the button, and the
3327 * id of the button that was clicked will be passed as the only parameter to the callback
3328 * (could also be the top-right close button).
3329 * @param {String} title The title bar text
3330 * @param {String} msg The message box body text
3331 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3332 * @param {Object} scope (optional) The scope of the callback function
3333 * @return {Roo.MessageBox} This message box
3335 alert : function(title, msg, fn, scope)
3350 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3351 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3352 * You are responsible for closing the message box when the process is complete.
3353 * @param {String} msg The message box body text
3354 * @param {String} title (optional) The title bar text
3355 * @return {Roo.MessageBox} This message box
3357 wait : function(msg, title){
3368 waitTimer = Roo.TaskMgr.start({
3370 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3378 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3379 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3380 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3381 * @param {String} title The title bar text
3382 * @param {String} msg The message box body text
3383 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3384 * @param {Object} scope (optional) The scope of the callback function
3385 * @return {Roo.MessageBox} This message box
3387 confirm : function(title, msg, fn, scope){
3391 buttons: this.YESNO,
3400 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3401 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3402 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3403 * (could also be the top-right close button) and the text that was entered will be passed as the two
3404 * parameters to the callback.
3405 * @param {String} title The title bar text
3406 * @param {String} msg The message box body text
3407 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3408 * @param {Object} scope (optional) The scope of the callback function
3409 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3410 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3411 * @return {Roo.MessageBox} This message box
3413 prompt : function(title, msg, fn, scope, multiline){
3417 buttons: this.OKCANCEL,
3422 multiline: multiline,
3429 * Button config that displays a single OK button
3434 * Button config that displays Yes and No buttons
3437 YESNO : {yes:true, no:true},
3439 * Button config that displays OK and Cancel buttons
3442 OKCANCEL : {ok:true, cancel:true},
3444 * Button config that displays Yes, No and Cancel buttons
3447 YESNOCANCEL : {yes:true, no:true, cancel:true},
3450 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3453 defaultTextHeight : 75,
3455 * The maximum width in pixels of the message box (defaults to 600)
3460 * The minimum width in pixels of the message box (defaults to 100)
3465 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3466 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3469 minProgressWidth : 250,
3471 * An object containing the default button text strings that can be overriden for localized language support.
3472 * Supported properties are: ok, cancel, yes and no.
3473 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3486 * Shorthand for {@link Roo.MessageBox}
3488 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3489 Roo.Msg = Roo.Msg || Roo.MessageBox;
3498 * @class Roo.bootstrap.Navbar
3499 * @extends Roo.bootstrap.Component
3500 * Bootstrap Navbar class
3503 * Create a new Navbar
3504 * @param {Object} config The config object
3508 Roo.bootstrap.Navbar = function(config){
3509 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3513 * @event beforetoggle
3514 * Fire before toggle the menu
3515 * @param {Roo.EventObject} e
3517 "beforetoggle" : true
3521 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3530 getAutoCreate : function(){
3533 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3537 initEvents :function ()
3539 //Roo.log(this.el.select('.navbar-toggle',true));
3540 this.el.select('.navbar-toggle',true).on('click', function() {
3541 if(this.fireEvent('beforetoggle', this) !== false){
3542 this.el.select('.navbar-collapse',true).toggleClass('in');
3552 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3554 var size = this.el.getSize();
3555 this.maskEl.setSize(size.width, size.height);
3556 this.maskEl.enableDisplayMode("block");
3565 getChildContainer : function()
3567 if (this.el.select('.collapse').getCount()) {
3568 return this.el.select('.collapse',true).first();
3601 * @class Roo.bootstrap.NavSimplebar
3602 * @extends Roo.bootstrap.Navbar
3603 * Bootstrap Sidebar class
3605 * @cfg {Boolean} inverse is inverted color
3607 * @cfg {String} type (nav | pills | tabs)
3608 * @cfg {Boolean} arrangement stacked | justified
3609 * @cfg {String} align (left | right) alignment
3611 * @cfg {Boolean} main (true|false) main nav bar? default false
3612 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3614 * @cfg {String} tag (header|footer|nav|div) default is nav
3620 * Create a new Sidebar
3621 * @param {Object} config The config object
3625 Roo.bootstrap.NavSimplebar = function(config){
3626 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3629 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3645 getAutoCreate : function(){
3649 tag : this.tag || 'div',
3662 this.type = this.type || 'nav';
3663 if (['tabs','pills'].indexOf(this.type)!==-1) {
3664 cfg.cn[0].cls += ' nav-' + this.type
3668 if (this.type!=='nav') {
3669 Roo.log('nav type must be nav/tabs/pills')
3671 cfg.cn[0].cls += ' navbar-nav'
3677 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3678 cfg.cn[0].cls += ' nav-' + this.arrangement;
3682 if (this.align === 'right') {
3683 cfg.cn[0].cls += ' navbar-right';
3687 cfg.cls += ' navbar-inverse';
3714 * @class Roo.bootstrap.NavHeaderbar
3715 * @extends Roo.bootstrap.NavSimplebar
3716 * Bootstrap Sidebar class
3718 * @cfg {String} brand what is brand
3719 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3720 * @cfg {String} brand_href href of the brand
3721 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3722 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3723 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3724 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3727 * Create a new Sidebar
3728 * @param {Object} config The config object
3732 Roo.bootstrap.NavHeaderbar = function(config){
3733 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3737 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3744 desktopCenter : false,
3747 getAutoCreate : function(){
3750 tag: this.nav || 'nav',
3757 if (this.desktopCenter) {
3758 cn.push({cls : 'container', cn : []});
3765 cls: 'navbar-header',
3770 cls: 'navbar-toggle',
3771 'data-toggle': 'collapse',
3776 html: 'Toggle navigation'
3798 cls: 'collapse navbar-collapse',
3802 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3804 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3805 cfg.cls += ' navbar-' + this.position;
3807 // tag can override this..
3809 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3812 if (this.brand !== '') {
3815 href: this.brand_href ? this.brand_href : '#',
3816 cls: 'navbar-brand',
3824 cfg.cls += ' main-nav';
3832 getHeaderChildContainer : function()
3834 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3835 return this.el.select('.navbar-header',true).first();
3838 return this.getChildContainer();
3842 initEvents : function()
3844 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3846 if (this.autohide) {
3851 Roo.get(document).on('scroll',function(e) {
3852 var ns = Roo.get(document).getScroll().top;
3853 var os = prevScroll;
3857 ft.removeClass('slideDown');
3858 ft.addClass('slideUp');
3861 ft.removeClass('slideUp');
3862 ft.addClass('slideDown');
3883 * @class Roo.bootstrap.NavSidebar
3884 * @extends Roo.bootstrap.Navbar
3885 * Bootstrap Sidebar class
3888 * Create a new Sidebar
3889 * @param {Object} config The config object
3893 Roo.bootstrap.NavSidebar = function(config){
3894 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3897 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3899 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3901 getAutoCreate : function(){
3906 cls: 'sidebar sidebar-nav'
3928 * @class Roo.bootstrap.NavGroup
3929 * @extends Roo.bootstrap.Component
3930 * Bootstrap NavGroup class
3931 * @cfg {String} align (left|right)
3932 * @cfg {Boolean} inverse
3933 * @cfg {String} type (nav|pills|tab) default nav
3934 * @cfg {String} navId - reference Id for navbar.
3938 * Create a new nav group
3939 * @param {Object} config The config object
3942 Roo.bootstrap.NavGroup = function(config){
3943 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3946 Roo.bootstrap.NavGroup.register(this);
3950 * Fires when the active item changes
3951 * @param {Roo.bootstrap.NavGroup} this
3952 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3953 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3960 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3971 getAutoCreate : function()
3973 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3980 if (['tabs','pills'].indexOf(this.type)!==-1) {
3981 cfg.cls += ' nav-' + this.type
3983 if (this.type!=='nav') {
3984 Roo.log('nav type must be nav/tabs/pills')
3986 cfg.cls += ' navbar-nav'
3989 if (this.parent().sidebar) {
3992 cls: 'dashboard-menu sidebar-menu'
3998 if (this.form === true) {
4004 if (this.align === 'right') {
4005 cfg.cls += ' navbar-right';
4007 cfg.cls += ' navbar-left';
4011 if (this.align === 'right') {
4012 cfg.cls += ' navbar-right';
4016 cfg.cls += ' navbar-inverse';
4024 * sets the active Navigation item
4025 * @param {Roo.bootstrap.NavItem} the new current navitem
4027 setActiveItem : function(item)
4030 Roo.each(this.navItems, function(v){
4035 v.setActive(false, true);
4042 item.setActive(true, true);
4043 this.fireEvent('changed', this, item, prev);
4048 * gets the active Navigation item
4049 * @return {Roo.bootstrap.NavItem} the current navitem
4051 getActive : function()
4055 Roo.each(this.navItems, function(v){
4066 indexOfNav : function()
4070 Roo.each(this.navItems, function(v,i){
4081 * adds a Navigation item
4082 * @param {Roo.bootstrap.NavItem} the navitem to add
4084 addItem : function(cfg)
4086 var cn = new Roo.bootstrap.NavItem(cfg);
4088 cn.parentId = this.id;
4089 cn.onRender(this.el, null);
4093 * register a Navigation item
4094 * @param {Roo.bootstrap.NavItem} the navitem to add
4096 register : function(item)
4098 this.navItems.push( item);
4099 item.navId = this.navId;
4104 * clear all the Navigation item
4107 clearAll : function()
4110 this.el.dom.innerHTML = '';
4113 getNavItem: function(tabId)
4116 Roo.each(this.navItems, function(e) {
4117 if (e.tabId == tabId) {
4127 setActiveNext : function()
4129 var i = this.indexOfNav(this.getActive());
4130 if (i > this.navItems.length) {
4133 this.setActiveItem(this.navItems[i+1]);
4135 setActivePrev : function()
4137 var i = this.indexOfNav(this.getActive());
4141 this.setActiveItem(this.navItems[i-1]);
4143 clearWasActive : function(except) {
4144 Roo.each(this.navItems, function(e) {
4145 if (e.tabId != except.tabId && e.was_active) {
4146 e.was_active = false;
4153 getWasActive : function ()
4156 Roo.each(this.navItems, function(e) {
4171 Roo.apply(Roo.bootstrap.NavGroup, {
4175 * register a Navigation Group
4176 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4178 register : function(navgrp)
4180 this.groups[navgrp.navId] = navgrp;
4184 * fetch a Navigation Group based on the navigation ID
4185 * @param {string} the navgroup to add
4186 * @returns {Roo.bootstrap.NavGroup} the navgroup
4188 get: function(navId) {
4189 if (typeof(this.groups[navId]) == 'undefined') {
4191 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4193 return this.groups[navId] ;
4208 * @class Roo.bootstrap.NavItem
4209 * @extends Roo.bootstrap.Component
4210 * Bootstrap Navbar.NavItem class
4211 * @cfg {String} href link to
4212 * @cfg {String} html content of button
4213 * @cfg {String} badge text inside badge
4214 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4215 * @cfg {String} glyphicon name of glyphicon
4216 * @cfg {String} icon name of font awesome icon
4217 * @cfg {Boolean} active Is item active
4218 * @cfg {Boolean} disabled Is item disabled
4220 * @cfg {Boolean} preventDefault (true | false) default false
4221 * @cfg {String} tabId the tab that this item activates.
4222 * @cfg {String} tagtype (a|span) render as a href or span?
4223 * @cfg {Boolean} animateRef (true|false) link to element default false
4226 * Create a new Navbar Item
4227 * @param {Object} config The config object
4229 Roo.bootstrap.NavItem = function(config){
4230 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4235 * The raw click event for the entire grid.
4236 * @param {Roo.EventObject} e
4241 * Fires when the active item active state changes
4242 * @param {Roo.bootstrap.NavItem} this
4243 * @param {boolean} state the new state
4249 * Fires when scroll to element
4250 * @param {Roo.bootstrap.NavItem} this
4251 * @param {Object} options
4252 * @param {Roo.EventObject} e
4260 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4268 preventDefault : false,
4275 getAutoCreate : function(){
4284 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4286 if (this.disabled) {
4287 cfg.cls += ' disabled';
4290 if (this.href || this.html || this.glyphicon || this.icon) {
4294 href : this.href || "#",
4295 html: this.html || ''
4300 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4303 if(this.glyphicon) {
4304 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4309 cfg.cn[0].html += " <span class='caret'></span>";
4313 if (this.badge !== '') {
4315 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4323 initEvents: function()
4325 if (typeof (this.menu) != 'undefined') {
4326 this.menu.parentType = this.xtype;
4327 this.menu.triggerEl = this.el;
4328 this.menu = this.addxtype(Roo.apply({}, this.menu));
4331 this.el.select('a',true).on('click', this.onClick, this);
4333 if(this.tagtype == 'span'){
4334 this.el.select('span',true).on('click', this.onClick, this);
4337 // at this point parent should be available..
4338 this.parent().register(this);
4341 onClick : function(e)
4343 if (e.getTarget('.dropdown-menu-item')) {
4344 // did you click on a menu itemm.... - then don't trigger onclick..
4349 this.preventDefault ||
4352 Roo.log("NavItem - prevent Default?");
4356 if (this.disabled) {
4360 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4361 if (tg && tg.transition) {
4362 Roo.log("waiting for the transitionend");
4368 //Roo.log("fire event clicked");
4369 if(this.fireEvent('click', this, e) === false){
4373 if(this.tagtype == 'span'){
4377 //Roo.log(this.href);
4378 var ael = this.el.select('a',true).first();
4381 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4382 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4383 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4384 return; // ignore... - it's a 'hash' to another page.
4386 Roo.log("NavItem - prevent Default?");
4388 this.scrollToElement(e);
4392 var p = this.parent();
4394 if (['tabs','pills'].indexOf(p.type)!==-1) {
4395 if (typeof(p.setActiveItem) !== 'undefined') {
4396 p.setActiveItem(this);
4400 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4401 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4402 // remove the collapsed menu expand...
4403 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4407 isActive: function () {
4410 setActive : function(state, fire, is_was_active)
4412 if (this.active && !state && this.navId) {
4413 this.was_active = true;
4414 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4416 nv.clearWasActive(this);
4420 this.active = state;
4423 this.el.removeClass('active');
4424 } else if (!this.el.hasClass('active')) {
4425 this.el.addClass('active');
4428 this.fireEvent('changed', this, state);
4431 // show a panel if it's registered and related..
4433 if (!this.navId || !this.tabId || !state || is_was_active) {
4437 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4441 var pan = tg.getPanelByName(this.tabId);
4445 // if we can not flip to new panel - go back to old nav highlight..
4446 if (false == tg.showPanel(pan)) {
4447 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4449 var onav = nv.getWasActive();
4451 onav.setActive(true, false, true);
4460 // this should not be here...
4461 setDisabled : function(state)
4463 this.disabled = state;
4465 this.el.removeClass('disabled');
4466 } else if (!this.el.hasClass('disabled')) {
4467 this.el.addClass('disabled');
4473 * Fetch the element to display the tooltip on.
4474 * @return {Roo.Element} defaults to this.el
4476 tooltipEl : function()
4478 return this.el.select('' + this.tagtype + '', true).first();
4481 scrollToElement : function(e)
4483 var c = document.body;
4486 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4488 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4489 c = document.documentElement;
4492 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4498 var o = target.calcOffsetsTo(c);
4505 this.fireEvent('scrollto', this, options, e);
4507 Roo.get(c).scrollTo('top', options.value, true);
4520 * <span> icon </span>
4521 * <span> text </span>
4522 * <span>badge </span>
4526 * @class Roo.bootstrap.NavSidebarItem
4527 * @extends Roo.bootstrap.NavItem
4528 * Bootstrap Navbar.NavSidebarItem class
4529 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4530 * {bool} open is the menu open
4532 * Create a new Navbar Button
4533 * @param {Object} config The config object
4535 Roo.bootstrap.NavSidebarItem = function(config){
4536 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4541 * The raw click event for the entire grid.
4542 * @param {Roo.EventObject} e
4547 * Fires when the active item active state changes
4548 * @param {Roo.bootstrap.NavSidebarItem} this
4549 * @param {boolean} state the new state
4557 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4559 badgeWeight : 'default',
4563 getAutoCreate : function(){
4568 href : this.href || '#',
4580 html : this.html || ''
4585 cfg.cls += ' active';
4588 if (this.disabled) {
4589 cfg.cls += ' disabled';
4592 cfg.cls += ' open x-open';
4595 if (this.glyphicon || this.icon) {
4596 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4597 a.cn.push({ tag : 'i', cls : c }) ;
4602 if (this.badge !== '') {
4604 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4608 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4609 a.cls += 'dropdown-toggle treeview' ;
4617 initEvents : function()
4619 if (typeof (this.menu) != 'undefined') {
4620 this.menu.parentType = this.xtype;
4621 this.menu.triggerEl = this.el;
4622 this.menu = this.addxtype(Roo.apply({}, this.menu));
4625 this.el.on('click', this.onClick, this);
4628 if(this.badge !== ''){
4630 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4635 onClick : function(e)
4642 if(this.preventDefault){
4646 this.fireEvent('click', this);
4649 disable : function()
4651 this.setDisabled(true);
4656 this.setDisabled(false);
4659 setDisabled : function(state)
4661 if(this.disabled == state){
4665 this.disabled = state;
4668 this.el.addClass('disabled');
4672 this.el.removeClass('disabled');
4677 setActive : function(state)
4679 if(this.active == state){
4683 this.active = state;
4686 this.el.addClass('active');
4690 this.el.removeClass('active');
4695 isActive: function ()
4700 setBadge : function(str)
4706 this.badgeEl.dom.innerHTML = str;
4723 * @class Roo.bootstrap.Row
4724 * @extends Roo.bootstrap.Component
4725 * Bootstrap Row class (contains columns...)
4729 * @param {Object} config The config object
4732 Roo.bootstrap.Row = function(config){
4733 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4736 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4738 getAutoCreate : function(){
4757 * @class Roo.bootstrap.Element
4758 * @extends Roo.bootstrap.Component
4759 * Bootstrap Element class
4760 * @cfg {String} html contents of the element
4761 * @cfg {String} tag tag of the element
4762 * @cfg {String} cls class of the element
4763 * @cfg {Boolean} preventDefault (true|false) default false
4764 * @cfg {Boolean} clickable (true|false) default false
4767 * Create a new Element
4768 * @param {Object} config The config object
4771 Roo.bootstrap.Element = function(config){
4772 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4778 * When a element is chick
4779 * @param {Roo.bootstrap.Element} this
4780 * @param {Roo.EventObject} e
4786 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4791 preventDefault: false,
4794 getAutoCreate : function(){
4805 initEvents: function()
4807 Roo.bootstrap.Element.superclass.initEvents.call(this);
4810 this.el.on('click', this.onClick, this);
4815 onClick : function(e)
4817 if(this.preventDefault){
4821 this.fireEvent('click', this, e);
4824 getValue : function()
4826 return this.el.dom.innerHTML;
4829 setValue : function(value)
4831 this.el.dom.innerHTML = value;
4846 * @class Roo.bootstrap.Pagination
4847 * @extends Roo.bootstrap.Component
4848 * Bootstrap Pagination class
4849 * @cfg {String} size xs | sm | md | lg
4850 * @cfg {Boolean} inverse false | true
4853 * Create a new Pagination
4854 * @param {Object} config The config object
4857 Roo.bootstrap.Pagination = function(config){
4858 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4861 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4867 getAutoCreate : function(){
4873 cfg.cls += ' inverse';
4879 cfg.cls += " " + this.cls;
4897 * @class Roo.bootstrap.PaginationItem
4898 * @extends Roo.bootstrap.Component
4899 * Bootstrap PaginationItem class
4900 * @cfg {String} html text
4901 * @cfg {String} href the link
4902 * @cfg {Boolean} preventDefault (true | false) default true
4903 * @cfg {Boolean} active (true | false) default false
4904 * @cfg {Boolean} disabled default false
4908 * Create a new PaginationItem
4909 * @param {Object} config The config object
4913 Roo.bootstrap.PaginationItem = function(config){
4914 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4919 * The raw click event for the entire grid.
4920 * @param {Roo.EventObject} e
4926 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4930 preventDefault: true,
4935 getAutoCreate : function(){
4941 href : this.href ? this.href : '#',
4942 html : this.html ? this.html : ''
4952 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4956 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4962 initEvents: function() {
4964 this.el.on('click', this.onClick, this);
4967 onClick : function(e)
4969 Roo.log('PaginationItem on click ');
4970 if(this.preventDefault){
4978 this.fireEvent('click', this, e);
4994 * @class Roo.bootstrap.Slider
4995 * @extends Roo.bootstrap.Component
4996 * Bootstrap Slider class
4999 * Create a new Slider
5000 * @param {Object} config The config object
5003 Roo.bootstrap.Slider = function(config){
5004 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5007 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5009 getAutoCreate : function(){
5013 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5017 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5029 * Ext JS Library 1.1.1
5030 * Copyright(c) 2006-2007, Ext JS, LLC.
5032 * Originally Released Under LGPL - original licence link has changed is not relivant.
5035 * <script type="text/javascript">
5040 * @class Roo.grid.ColumnModel
5041 * @extends Roo.util.Observable
5042 * This is the default implementation of a ColumnModel used by the Grid. It defines
5043 * the columns in the grid.
5046 var colModel = new Roo.grid.ColumnModel([
5047 {header: "Ticker", width: 60, sortable: true, locked: true},
5048 {header: "Company Name", width: 150, sortable: true},
5049 {header: "Market Cap.", width: 100, sortable: true},
5050 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5051 {header: "Employees", width: 100, sortable: true, resizable: false}
5056 * The config options listed for this class are options which may appear in each
5057 * individual column definition.
5058 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5060 * @param {Object} config An Array of column config objects. See this class's
5061 * config objects for details.
5063 Roo.grid.ColumnModel = function(config){
5065 * The config passed into the constructor
5067 this.config = config;
5070 // if no id, create one
5071 // if the column does not have a dataIndex mapping,
5072 // map it to the order it is in the config
5073 for(var i = 0, len = config.length; i < len; i++){
5075 if(typeof c.dataIndex == "undefined"){
5078 if(typeof c.renderer == "string"){
5079 c.renderer = Roo.util.Format[c.renderer];
5081 if(typeof c.id == "undefined"){
5084 if(c.editor && c.editor.xtype){
5085 c.editor = Roo.factory(c.editor, Roo.grid);
5087 if(c.editor && c.editor.isFormField){
5088 c.editor = new Roo.grid.GridEditor(c.editor);
5090 this.lookup[c.id] = c;
5094 * The width of columns which have no width specified (defaults to 100)
5097 this.defaultWidth = 100;
5100 * Default sortable of columns which have no sortable specified (defaults to false)
5103 this.defaultSortable = false;
5107 * @event widthchange
5108 * Fires when the width of a column changes.
5109 * @param {ColumnModel} this
5110 * @param {Number} columnIndex The column index
5111 * @param {Number} newWidth The new width
5113 "widthchange": true,
5115 * @event headerchange
5116 * Fires when the text of a header changes.
5117 * @param {ColumnModel} this
5118 * @param {Number} columnIndex The column index
5119 * @param {Number} newText The new header text
5121 "headerchange": true,
5123 * @event hiddenchange
5124 * Fires when a column is hidden or "unhidden".
5125 * @param {ColumnModel} this
5126 * @param {Number} columnIndex The column index
5127 * @param {Boolean} hidden true if hidden, false otherwise
5129 "hiddenchange": true,
5131 * @event columnmoved
5132 * Fires when a column is moved.
5133 * @param {ColumnModel} this
5134 * @param {Number} oldIndex
5135 * @param {Number} newIndex
5137 "columnmoved" : true,
5139 * @event columlockchange
5140 * Fires when a column's locked state is changed
5141 * @param {ColumnModel} this
5142 * @param {Number} colIndex
5143 * @param {Boolean} locked true if locked
5145 "columnlockchange" : true
5147 Roo.grid.ColumnModel.superclass.constructor.call(this);
5149 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5151 * @cfg {String} header The header text to display in the Grid view.
5154 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5155 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5156 * specified, the column's index is used as an index into the Record's data Array.
5159 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5160 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5163 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5164 * Defaults to the value of the {@link #defaultSortable} property.
5165 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5168 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5171 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5174 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5177 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5180 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5181 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5182 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5183 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5186 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5189 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5192 * @cfg {String} cursor (Optional)
5195 * @cfg {String} tooltip (Optional)
5198 * @cfg {Number} xs (Optional)
5201 * @cfg {Number} sm (Optional)
5204 * @cfg {Number} md (Optional)
5207 * @cfg {Number} lg (Optional)
5210 * Returns the id of the column at the specified index.
5211 * @param {Number} index The column index
5212 * @return {String} the id
5214 getColumnId : function(index){
5215 return this.config[index].id;
5219 * Returns the column for a specified id.
5220 * @param {String} id The column id
5221 * @return {Object} the column
5223 getColumnById : function(id){
5224 return this.lookup[id];
5229 * Returns the column for a specified dataIndex.
5230 * @param {String} dataIndex The column dataIndex
5231 * @return {Object|Boolean} the column or false if not found
5233 getColumnByDataIndex: function(dataIndex){
5234 var index = this.findColumnIndex(dataIndex);
5235 return index > -1 ? this.config[index] : false;
5239 * Returns the index for a specified column id.
5240 * @param {String} id The column id
5241 * @return {Number} the index, or -1 if not found
5243 getIndexById : function(id){
5244 for(var i = 0, len = this.config.length; i < len; i++){
5245 if(this.config[i].id == id){
5253 * Returns the index for a specified column dataIndex.
5254 * @param {String} dataIndex The column dataIndex
5255 * @return {Number} the index, or -1 if not found
5258 findColumnIndex : function(dataIndex){
5259 for(var i = 0, len = this.config.length; i < len; i++){
5260 if(this.config[i].dataIndex == dataIndex){
5268 moveColumn : function(oldIndex, newIndex){
5269 var c = this.config[oldIndex];
5270 this.config.splice(oldIndex, 1);
5271 this.config.splice(newIndex, 0, c);
5272 this.dataMap = null;
5273 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5276 isLocked : function(colIndex){
5277 return this.config[colIndex].locked === true;
5280 setLocked : function(colIndex, value, suppressEvent){
5281 if(this.isLocked(colIndex) == value){
5284 this.config[colIndex].locked = value;
5286 this.fireEvent("columnlockchange", this, colIndex, value);
5290 getTotalLockedWidth : function(){
5292 for(var i = 0; i < this.config.length; i++){
5293 if(this.isLocked(i) && !this.isHidden(i)){
5294 this.totalWidth += this.getColumnWidth(i);
5300 getLockedCount : function(){
5301 for(var i = 0, len = this.config.length; i < len; i++){
5302 if(!this.isLocked(i)){
5307 return this.config.length;
5311 * Returns the number of columns.
5314 getColumnCount : function(visibleOnly){
5315 if(visibleOnly === true){
5317 for(var i = 0, len = this.config.length; i < len; i++){
5318 if(!this.isHidden(i)){
5324 return this.config.length;
5328 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5329 * @param {Function} fn
5330 * @param {Object} scope (optional)
5331 * @return {Array} result
5333 getColumnsBy : function(fn, scope){
5335 for(var i = 0, len = this.config.length; i < len; i++){
5336 var c = this.config[i];
5337 if(fn.call(scope||this, c, i) === true){
5345 * Returns true if the specified column is sortable.
5346 * @param {Number} col The column index
5349 isSortable : function(col){
5350 if(typeof this.config[col].sortable == "undefined"){
5351 return this.defaultSortable;
5353 return this.config[col].sortable;
5357 * Returns the rendering (formatting) function defined for the column.
5358 * @param {Number} col The column index.
5359 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5361 getRenderer : function(col){
5362 if(!this.config[col].renderer){
5363 return Roo.grid.ColumnModel.defaultRenderer;
5365 return this.config[col].renderer;
5369 * Sets the rendering (formatting) function for a column.
5370 * @param {Number} col The column index
5371 * @param {Function} fn The function to use to process the cell's raw data
5372 * to return HTML markup for the grid view. The render function is called with
5373 * the following parameters:<ul>
5374 * <li>Data value.</li>
5375 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5376 * <li>css A CSS style string to apply to the table cell.</li>
5377 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5378 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5379 * <li>Row index</li>
5380 * <li>Column index</li>
5381 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5383 setRenderer : function(col, fn){
5384 this.config[col].renderer = fn;
5388 * Returns the width for the specified column.
5389 * @param {Number} col The column index
5392 getColumnWidth : function(col){
5393 return this.config[col].width * 1 || this.defaultWidth;
5397 * Sets the width for a column.
5398 * @param {Number} col The column index
5399 * @param {Number} width The new width
5401 setColumnWidth : function(col, width, suppressEvent){
5402 this.config[col].width = width;
5403 this.totalWidth = null;
5405 this.fireEvent("widthchange", this, col, width);
5410 * Returns the total width of all columns.
5411 * @param {Boolean} includeHidden True to include hidden column widths
5414 getTotalWidth : function(includeHidden){
5415 if(!this.totalWidth){
5416 this.totalWidth = 0;
5417 for(var i = 0, len = this.config.length; i < len; i++){
5418 if(includeHidden || !this.isHidden(i)){
5419 this.totalWidth += this.getColumnWidth(i);
5423 return this.totalWidth;
5427 * Returns the header for the specified column.
5428 * @param {Number} col The column index
5431 getColumnHeader : function(col){
5432 return this.config[col].header;
5436 * Sets the header for a column.
5437 * @param {Number} col The column index
5438 * @param {String} header The new header
5440 setColumnHeader : function(col, header){
5441 this.config[col].header = header;
5442 this.fireEvent("headerchange", this, col, header);
5446 * Returns the tooltip for the specified column.
5447 * @param {Number} col The column index
5450 getColumnTooltip : function(col){
5451 return this.config[col].tooltip;
5454 * Sets the tooltip for a column.
5455 * @param {Number} col The column index
5456 * @param {String} tooltip The new tooltip
5458 setColumnTooltip : function(col, tooltip){
5459 this.config[col].tooltip = tooltip;
5463 * Returns the dataIndex for the specified column.
5464 * @param {Number} col The column index
5467 getDataIndex : function(col){
5468 return this.config[col].dataIndex;
5472 * Sets the dataIndex for a column.
5473 * @param {Number} col The column index
5474 * @param {Number} dataIndex The new dataIndex
5476 setDataIndex : function(col, dataIndex){
5477 this.config[col].dataIndex = dataIndex;
5483 * Returns true if the cell is editable.
5484 * @param {Number} colIndex The column index
5485 * @param {Number} rowIndex The row index - this is nto actually used..?
5488 isCellEditable : function(colIndex, rowIndex){
5489 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5493 * Returns the editor defined for the cell/column.
5494 * return false or null to disable editing.
5495 * @param {Number} colIndex The column index
5496 * @param {Number} rowIndex The row index
5499 getCellEditor : function(colIndex, rowIndex){
5500 return this.config[colIndex].editor;
5504 * Sets if a column is editable.
5505 * @param {Number} col The column index
5506 * @param {Boolean} editable True if the column is editable
5508 setEditable : function(col, editable){
5509 this.config[col].editable = editable;
5514 * Returns true if the column is hidden.
5515 * @param {Number} colIndex The column index
5518 isHidden : function(colIndex){
5519 return this.config[colIndex].hidden;
5524 * Returns true if the column width cannot be changed
5526 isFixed : function(colIndex){
5527 return this.config[colIndex].fixed;
5531 * Returns true if the column can be resized
5534 isResizable : function(colIndex){
5535 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5538 * Sets if a column is hidden.
5539 * @param {Number} colIndex The column index
5540 * @param {Boolean} hidden True if the column is hidden
5542 setHidden : function(colIndex, hidden){
5543 this.config[colIndex].hidden = hidden;
5544 this.totalWidth = null;
5545 this.fireEvent("hiddenchange", this, colIndex, hidden);
5549 * Sets the editor for a column.
5550 * @param {Number} col The column index
5551 * @param {Object} editor The editor object
5553 setEditor : function(col, editor){
5554 this.config[col].editor = editor;
5558 Roo.grid.ColumnModel.defaultRenderer = function(value)
5560 if(typeof value == "object") {
5563 if(typeof value == "string" && value.length < 1){
5567 return String.format("{0}", value);
5570 // Alias for backwards compatibility
5571 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5574 * Ext JS Library 1.1.1
5575 * Copyright(c) 2006-2007, Ext JS, LLC.
5577 * Originally Released Under LGPL - original licence link has changed is not relivant.
5580 * <script type="text/javascript">
5584 * @class Roo.LoadMask
5585 * A simple utility class for generically masking elements while loading data. If the element being masked has
5586 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5587 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5588 * element's UpdateManager load indicator and will be destroyed after the initial load.
5590 * Create a new LoadMask
5591 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5592 * @param {Object} config The config object
5594 Roo.LoadMask = function(el, config){
5595 this.el = Roo.get(el);
5596 Roo.apply(this, config);
5598 this.store.on('beforeload', this.onBeforeLoad, this);
5599 this.store.on('load', this.onLoad, this);
5600 this.store.on('loadexception', this.onLoadException, this);
5601 this.removeMask = false;
5603 var um = this.el.getUpdateManager();
5604 um.showLoadIndicator = false; // disable the default indicator
5605 um.on('beforeupdate', this.onBeforeLoad, this);
5606 um.on('update', this.onLoad, this);
5607 um.on('failure', this.onLoad, this);
5608 this.removeMask = true;
5612 Roo.LoadMask.prototype = {
5614 * @cfg {Boolean} removeMask
5615 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5616 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5620 * The text to display in a centered loading message box (defaults to 'Loading...')
5624 * @cfg {String} msgCls
5625 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5627 msgCls : 'x-mask-loading',
5630 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5636 * Disables the mask to prevent it from being displayed
5638 disable : function(){
5639 this.disabled = true;
5643 * Enables the mask so that it can be displayed
5645 enable : function(){
5646 this.disabled = false;
5649 onLoadException : function()
5653 if (typeof(arguments[3]) != 'undefined') {
5654 Roo.MessageBox.alert("Error loading",arguments[3]);
5658 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5659 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5668 this.el.unmask(this.removeMask);
5673 this.el.unmask(this.removeMask);
5677 onBeforeLoad : function(){
5679 this.el.mask(this.msg, this.msgCls);
5684 destroy : function(){
5686 this.store.un('beforeload', this.onBeforeLoad, this);
5687 this.store.un('load', this.onLoad, this);
5688 this.store.un('loadexception', this.onLoadException, this);
5690 var um = this.el.getUpdateManager();
5691 um.un('beforeupdate', this.onBeforeLoad, this);
5692 um.un('update', this.onLoad, this);
5693 um.un('failure', this.onLoad, this);
5704 * @class Roo.bootstrap.Table
5705 * @extends Roo.bootstrap.Component
5706 * Bootstrap Table class
5707 * @cfg {String} cls table class
5708 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5709 * @cfg {String} bgcolor Specifies the background color for a table
5710 * @cfg {Number} border Specifies whether the table cells should have borders or not
5711 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5712 * @cfg {Number} cellspacing Specifies the space between cells
5713 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5714 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5715 * @cfg {String} sortable Specifies that the table should be sortable
5716 * @cfg {String} summary Specifies a summary of the content of a table
5717 * @cfg {Number} width Specifies the width of a table
5718 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5720 * @cfg {boolean} striped Should the rows be alternative striped
5721 * @cfg {boolean} bordered Add borders to the table
5722 * @cfg {boolean} hover Add hover highlighting
5723 * @cfg {boolean} condensed Format condensed
5724 * @cfg {boolean} responsive Format condensed
5725 * @cfg {Boolean} loadMask (true|false) default false
5726 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5727 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5728 * @cfg {Boolean} rowSelection (true|false) default false
5729 * @cfg {Boolean} cellSelection (true|false) default false
5730 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5731 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5735 * Create a new Table
5736 * @param {Object} config The config object
5739 Roo.bootstrap.Table = function(config){
5740 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5745 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5746 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5747 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5748 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5750 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5752 this.sm.grid = this;
5753 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5754 this.sm = this.selModel;
5755 this.sm.xmodule = this.xmodule || false;
5758 if (this.cm && typeof(this.cm.config) == 'undefined') {
5759 this.colModel = new Roo.grid.ColumnModel(this.cm);
5760 this.cm = this.colModel;
5761 this.cm.xmodule = this.xmodule || false;
5764 this.store= Roo.factory(this.store, Roo.data);
5765 this.ds = this.store;
5766 this.ds.xmodule = this.xmodule || false;
5769 if (this.footer && this.store) {
5770 this.footer.dataSource = this.ds;
5771 this.footer = Roo.factory(this.footer);
5778 * Fires when a cell is clicked
5779 * @param {Roo.bootstrap.Table} this
5780 * @param {Roo.Element} el
5781 * @param {Number} rowIndex
5782 * @param {Number} columnIndex
5783 * @param {Roo.EventObject} e
5787 * @event celldblclick
5788 * Fires when a cell is double clicked
5789 * @param {Roo.bootstrap.Table} this
5790 * @param {Roo.Element} el
5791 * @param {Number} rowIndex
5792 * @param {Number} columnIndex
5793 * @param {Roo.EventObject} e
5795 "celldblclick" : true,
5798 * Fires when a row is clicked
5799 * @param {Roo.bootstrap.Table} this
5800 * @param {Roo.Element} el
5801 * @param {Number} rowIndex
5802 * @param {Roo.EventObject} e
5806 * @event rowdblclick
5807 * Fires when a row is double clicked
5808 * @param {Roo.bootstrap.Table} this
5809 * @param {Roo.Element} el
5810 * @param {Number} rowIndex
5811 * @param {Roo.EventObject} e
5813 "rowdblclick" : true,
5816 * Fires when a mouseover occur
5817 * @param {Roo.bootstrap.Table} this
5818 * @param {Roo.Element} el
5819 * @param {Number} rowIndex
5820 * @param {Number} columnIndex
5821 * @param {Roo.EventObject} e
5826 * Fires when a mouseout occur
5827 * @param {Roo.bootstrap.Table} this
5828 * @param {Roo.Element} el
5829 * @param {Number} rowIndex
5830 * @param {Number} columnIndex
5831 * @param {Roo.EventObject} e
5836 * Fires when a row is rendered, so you can change add a style to it.
5837 * @param {Roo.bootstrap.Table} this
5838 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5842 * @event rowsrendered
5843 * Fires when all the rows have been rendered
5844 * @param {Roo.bootstrap.Table} this
5846 'rowsrendered' : true,
5848 * @event contextmenu
5849 * The raw contextmenu event for the entire grid.
5850 * @param {Roo.EventObject} e
5852 "contextmenu" : true,
5854 * @event rowcontextmenu
5855 * Fires when a row is right clicked
5856 * @param {Roo.bootstrap.Table} this
5857 * @param {Number} rowIndex
5858 * @param {Roo.EventObject} e
5860 "rowcontextmenu" : true,
5862 * @event cellcontextmenu
5863 * Fires when a cell is right clicked
5864 * @param {Roo.bootstrap.Table} this
5865 * @param {Number} rowIndex
5866 * @param {Number} cellIndex
5867 * @param {Roo.EventObject} e
5869 "cellcontextmenu" : true,
5871 * @event headercontextmenu
5872 * Fires when a header is right clicked
5873 * @param {Roo.bootstrap.Table} this
5874 * @param {Number} columnIndex
5875 * @param {Roo.EventObject} e
5877 "headercontextmenu" : true
5881 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5907 rowSelection : false,
5908 cellSelection : false,
5911 // Roo.Element - the tbody
5913 // Roo.Element - thead element
5916 container: false, // used by gridpanel...
5918 getAutoCreate : function()
5920 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5927 if (this.scrollBody) {
5928 cfg.cls += ' table-body-fixed';
5931 cfg.cls += ' table-striped';
5935 cfg.cls += ' table-hover';
5937 if (this.bordered) {
5938 cfg.cls += ' table-bordered';
5940 if (this.condensed) {
5941 cfg.cls += ' table-condensed';
5943 if (this.responsive) {
5944 cfg.cls += ' table-responsive';
5948 cfg.cls+= ' ' +this.cls;
5951 // this lot should be simplifed...
5954 cfg.align=this.align;
5957 cfg.bgcolor=this.bgcolor;
5960 cfg.border=this.border;
5962 if (this.cellpadding) {
5963 cfg.cellpadding=this.cellpadding;
5965 if (this.cellspacing) {
5966 cfg.cellspacing=this.cellspacing;
5969 cfg.frame=this.frame;
5972 cfg.rules=this.rules;
5974 if (this.sortable) {
5975 cfg.sortable=this.sortable;
5978 cfg.summary=this.summary;
5981 cfg.width=this.width;
5984 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5987 if(this.store || this.cm){
5988 if(this.headerShow){
5989 cfg.cn.push(this.renderHeader());
5992 cfg.cn.push(this.renderBody());
5994 if(this.footerShow){
5995 cfg.cn.push(this.renderFooter());
5997 // where does this come from?
5998 //cfg.cls+= ' TableGrid';
6001 return { cn : [ cfg ] };
6004 initEvents : function()
6006 if(!this.store || !this.cm){
6009 if (this.selModel) {
6010 this.selModel.initEvents();
6014 //Roo.log('initEvents with ds!!!!');
6016 this.mainBody = this.el.select('tbody', true).first();
6017 this.mainHead = this.el.select('thead', true).first();
6024 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6025 e.on('click', _this.sort, _this);
6028 this.mainBody.on("click", this.onClick, this);
6029 this.mainBody.on("dblclick", this.onDblClick, this);
6031 // why is this done????? = it breaks dialogs??
6032 //this.parent().el.setStyle('position', 'relative');
6036 this.footer.parentId = this.id;
6037 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6040 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6042 this.store.on('load', this.onLoad, this);
6043 this.store.on('beforeload', this.onBeforeLoad, this);
6044 this.store.on('update', this.onUpdate, this);
6045 this.store.on('add', this.onAdd, this);
6046 this.store.on("clear", this.clear, this);
6048 this.el.on("contextmenu", this.onContextMenu, this);
6050 this.mainBody.on('scroll', this.onBodyScroll, this);
6055 onContextMenu : function(e, t)
6057 this.processEvent("contextmenu", e);
6060 processEvent : function(name, e)
6062 if (name != 'touchstart' ) {
6063 this.fireEvent(name, e);
6066 var t = e.getTarget();
6068 var cell = Roo.get(t);
6074 if(cell.findParent('tfoot', false, true)){
6078 if(cell.findParent('thead', false, true)){
6080 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6081 cell = Roo.get(t).findParent('th', false, true);
6083 Roo.log("failed to find th in thead?");
6084 Roo.log(e.getTarget());
6089 var cellIndex = cell.dom.cellIndex;
6091 var ename = name == 'touchstart' ? 'click' : name;
6092 this.fireEvent("header" + ename, this, cellIndex, e);
6097 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6098 cell = Roo.get(t).findParent('td', false, true);
6100 Roo.log("failed to find th in tbody?");
6101 Roo.log(e.getTarget());
6106 var row = cell.findParent('tr', false, true);
6107 var cellIndex = cell.dom.cellIndex;
6108 var rowIndex = row.dom.rowIndex - 1;
6112 this.fireEvent("row" + name, this, rowIndex, e);
6116 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6122 onMouseover : 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('mouseover', this, cell, rowIndex, cellIndex, e);
6142 onMouseout : function(e, el)
6144 var cell = Roo.get(el);
6150 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6151 cell = cell.findParent('td', false, true);
6154 var row = cell.findParent('tr', false, true);
6155 var cellIndex = cell.dom.cellIndex;
6156 var rowIndex = row.dom.rowIndex - 1; // start from 0
6158 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6162 onClick : function(e, el)
6164 var cell = Roo.get(el);
6166 if(!cell || (!this.cellSelection && !this.rowSelection)){
6170 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6171 cell = cell.findParent('td', false, true);
6174 if(!cell || typeof(cell) == 'undefined'){
6178 var row = cell.findParent('tr', false, true);
6180 if(!row || typeof(row) == 'undefined'){
6184 var cellIndex = cell.dom.cellIndex;
6185 var rowIndex = this.getRowIndex(row);
6187 // why??? - should these not be based on SelectionModel?
6188 if(this.cellSelection){
6189 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6192 if(this.rowSelection){
6193 this.fireEvent('rowclick', this, row, rowIndex, e);
6199 onDblClick : function(e,el)
6201 var cell = Roo.get(el);
6203 if(!cell || (!this.cellSelection && !this.rowSelection)){
6207 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6208 cell = cell.findParent('td', false, true);
6211 if(!cell || typeof(cell) == 'undefined'){
6215 var row = cell.findParent('tr', false, true);
6217 if(!row || typeof(row) == 'undefined'){
6221 var cellIndex = cell.dom.cellIndex;
6222 var rowIndex = this.getRowIndex(row);
6224 if(this.cellSelection){
6225 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6228 if(this.rowSelection){
6229 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6233 sort : function(e,el)
6235 var col = Roo.get(el);
6237 if(!col.hasClass('sortable')){
6241 var sort = col.attr('sort');
6244 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6248 this.store.sortInfo = {field : sort, direction : dir};
6251 Roo.log("calling footer first");
6252 this.footer.onClick('first');
6255 this.store.load({ params : { start : 0 } });
6259 renderHeader : function()
6267 this.totalWidth = 0;
6269 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6271 var config = cm.config[i];
6276 html: cm.getColumnHeader(i)
6281 if(typeof(config.sortable) != 'undefined' && config.sortable){
6283 c.html = '<i class="glyphicon"></i>' + c.html;
6286 if(typeof(config.lgHeader) != 'undefined'){
6287 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6290 if(typeof(config.mdHeader) != 'undefined'){
6291 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6294 if(typeof(config.smHeader) != 'undefined'){
6295 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6298 if(typeof(config.xsHeader) != 'undefined'){
6299 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6306 if(typeof(config.tooltip) != 'undefined'){
6307 c.tooltip = config.tooltip;
6310 if(typeof(config.colspan) != 'undefined'){
6311 c.colspan = config.colspan;
6314 if(typeof(config.hidden) != 'undefined' && config.hidden){
6315 c.style += ' display:none;';
6318 if(typeof(config.dataIndex) != 'undefined'){
6319 c.sort = config.dataIndex;
6324 if(typeof(config.align) != 'undefined' && config.align.length){
6325 c.style += ' text-align:' + config.align + ';';
6328 if(typeof(config.width) != 'undefined'){
6329 c.style += ' width:' + config.width + 'px;';
6330 this.totalWidth += config.width;
6332 this.totalWidth += 100; // assume minimum of 100 per column?
6335 if(typeof(config.cls) != 'undefined'){
6336 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6339 ['xs','sm','md','lg'].map(function(size){
6341 if(typeof(config[size]) == 'undefined'){
6345 if (!config[size]) { // 0 = hidden
6346 c.cls += ' hidden-' + size;
6350 c.cls += ' col-' + size + '-' + config[size];
6360 renderBody : function()
6370 colspan : this.cm.getColumnCount()
6380 renderFooter : function()
6390 colspan : this.cm.getColumnCount()
6404 // Roo.log('ds onload');
6409 var ds = this.store;
6411 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6412 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6413 if (_this.store.sortInfo) {
6415 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6416 e.select('i', true).addClass(['glyphicon-arrow-up']);
6419 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6420 e.select('i', true).addClass(['glyphicon-arrow-down']);
6425 var tbody = this.mainBody;
6427 if(ds.getCount() > 0){
6428 ds.data.each(function(d,rowIndex){
6429 var row = this.renderRow(cm, ds, rowIndex);
6431 tbody.createChild(row);
6435 if(row.cellObjects.length){
6436 Roo.each(row.cellObjects, function(r){
6437 _this.renderCellObject(r);
6444 Roo.each(this.el.select('tbody td', true).elements, function(e){
6445 e.on('mouseover', _this.onMouseover, _this);
6448 Roo.each(this.el.select('tbody td', true).elements, function(e){
6449 e.on('mouseout', _this.onMouseout, _this);
6451 this.fireEvent('rowsrendered', this);
6452 //if(this.loadMask){
6453 // this.maskEl.hide();
6460 onUpdate : function(ds,record)
6462 this.refreshRow(record);
6466 onRemove : function(ds, record, index, isUpdate){
6467 if(isUpdate !== true){
6468 this.fireEvent("beforerowremoved", this, index, record);
6470 var bt = this.mainBody.dom;
6472 var rows = this.el.select('tbody > tr', true).elements;
6474 if(typeof(rows[index]) != 'undefined'){
6475 bt.removeChild(rows[index].dom);
6478 // if(bt.rows[index]){
6479 // bt.removeChild(bt.rows[index]);
6482 if(isUpdate !== true){
6483 //this.stripeRows(index);
6484 //this.syncRowHeights(index, index);
6486 this.fireEvent("rowremoved", this, index, record);
6490 onAdd : function(ds, records, rowIndex)
6492 //Roo.log('on Add called');
6493 // - note this does not handle multiple adding very well..
6494 var bt = this.mainBody.dom;
6495 for (var i =0 ; i < records.length;i++) {
6496 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6497 //Roo.log(records[i]);
6498 //Roo.log(this.store.getAt(rowIndex+i));
6499 this.insertRow(this.store, rowIndex + i, false);
6506 refreshRow : function(record){
6507 var ds = this.store, index;
6508 if(typeof record == 'number'){
6510 record = ds.getAt(index);
6512 index = ds.indexOf(record);
6514 this.insertRow(ds, index, true);
6516 this.onRemove(ds, record, index+1, true);
6518 //this.syncRowHeights(index, index);
6520 this.fireEvent("rowupdated", this, index, record);
6523 insertRow : function(dm, rowIndex, isUpdate){
6526 this.fireEvent("beforerowsinserted", this, rowIndex);
6528 //var s = this.getScrollState();
6529 var row = this.renderRow(this.cm, this.store, rowIndex);
6530 // insert before rowIndex..
6531 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6535 if(row.cellObjects.length){
6536 Roo.each(row.cellObjects, function(r){
6537 _this.renderCellObject(r);
6542 this.fireEvent("rowsinserted", this, rowIndex);
6543 //this.syncRowHeights(firstRow, lastRow);
6544 //this.stripeRows(firstRow);
6551 getRowDom : function(rowIndex)
6553 var rows = this.el.select('tbody > tr', true).elements;
6555 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6558 // returns the object tree for a tr..
6561 renderRow : function(cm, ds, rowIndex)
6564 var d = ds.getAt(rowIndex);
6571 var cellObjects = [];
6573 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6574 var config = cm.config[i];
6576 var renderer = cm.getRenderer(i);
6580 if(typeof(renderer) !== 'undefined'){
6581 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6583 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6584 // and are rendered into the cells after the row is rendered - using the id for the element.
6586 if(typeof(value) === 'object'){
6596 rowIndex : rowIndex,
6601 this.fireEvent('rowclass', this, rowcfg);
6605 cls : rowcfg.rowClass,
6607 html: (typeof(value) === 'object') ? '' : value
6614 if(typeof(config.colspan) != 'undefined'){
6615 td.colspan = config.colspan;
6618 if(typeof(config.hidden) != 'undefined' && config.hidden){
6619 td.style += ' display:none;';
6622 if(typeof(config.align) != 'undefined' && config.align.length){
6623 td.style += ' text-align:' + config.align + ';';
6626 if(typeof(config.width) != 'undefined'){
6627 td.style += ' width:' + config.width + 'px;';
6630 if(typeof(config.cursor) != 'undefined'){
6631 td.style += ' cursor:' + config.cursor + ';';
6634 if(typeof(config.cls) != 'undefined'){
6635 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6638 ['xs','sm','md','lg'].map(function(size){
6640 if(typeof(config[size]) == 'undefined'){
6644 if (!config[size]) { // 0 = hidden
6645 td.cls += ' hidden-' + size;
6649 td.cls += ' col-' + size + '-' + config[size];
6657 row.cellObjects = cellObjects;
6665 onBeforeLoad : function()
6667 //Roo.log('ds onBeforeLoad');
6671 //if(this.loadMask){
6672 // this.maskEl.show();
6680 this.el.select('tbody', true).first().dom.innerHTML = '';
6683 * Show or hide a row.
6684 * @param {Number} rowIndex to show or hide
6685 * @param {Boolean} state hide
6687 setRowVisibility : function(rowIndex, state)
6689 var bt = this.mainBody.dom;
6691 var rows = this.el.select('tbody > tr', true).elements;
6693 if(typeof(rows[rowIndex]) == 'undefined'){
6696 rows[rowIndex].dom.style.display = state ? '' : 'none';
6700 getSelectionModel : function(){
6702 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6704 return this.selModel;
6707 * Render the Roo.bootstrap object from renderder
6709 renderCellObject : function(r)
6713 var t = r.cfg.render(r.container);
6716 Roo.each(r.cfg.cn, function(c){
6718 container: t.getChildContainer(),
6721 _this.renderCellObject(child);
6726 getRowIndex : function(row)
6730 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6741 * Returns the grid's underlying element = used by panel.Grid
6742 * @return {Element} The element
6744 getGridEl : function(){
6748 * Forces a resize - used by panel.Grid
6749 * @return {Element} The element
6751 autoSize : function()
6753 //var ctr = Roo.get(this.container.dom.parentElement);
6754 var ctr = Roo.get(this.el.dom);
6756 var thd = this.getGridEl().select('thead',true).first();
6757 var tbd = this.getGridEl().select('tbody', true).first();
6758 var tfd = this.getGridEl().select('tfoot', true).first();
6760 var cw = ctr.getWidth();
6764 tbd.setSize(ctr.getWidth(),
6765 ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6767 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6770 cw = Math.max(cw, this.totalWidth);
6771 this.getGridEl().select('tr',true).setWidth(cw);
6772 // resize 'expandable coloumn?
6774 return; // we doe not have a view in this design..
6777 onBodyScroll: function()
6780 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6781 this.mainHead.setStyle({
6782 'position' : 'relative',
6783 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6800 * @class Roo.bootstrap.TableCell
6801 * @extends Roo.bootstrap.Component
6802 * Bootstrap TableCell class
6803 * @cfg {String} html cell contain text
6804 * @cfg {String} cls cell class
6805 * @cfg {String} tag cell tag (td|th) default td
6806 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6807 * @cfg {String} align Aligns the content in a cell
6808 * @cfg {String} axis Categorizes cells
6809 * @cfg {String} bgcolor Specifies the background color of a cell
6810 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6811 * @cfg {Number} colspan Specifies the number of columns a cell should span
6812 * @cfg {String} headers Specifies one or more header cells a cell is related to
6813 * @cfg {Number} height Sets the height of a cell
6814 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6815 * @cfg {Number} rowspan Sets the number of rows a cell should span
6816 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6817 * @cfg {String} valign Vertical aligns the content in a cell
6818 * @cfg {Number} width Specifies the width of a cell
6821 * Create a new TableCell
6822 * @param {Object} config The config object
6825 Roo.bootstrap.TableCell = function(config){
6826 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6829 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6849 getAutoCreate : function(){
6850 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6870 cfg.align=this.align
6876 cfg.bgcolor=this.bgcolor
6879 cfg.charoff=this.charoff
6882 cfg.colspan=this.colspan
6885 cfg.headers=this.headers
6888 cfg.height=this.height
6891 cfg.nowrap=this.nowrap
6894 cfg.rowspan=this.rowspan
6897 cfg.scope=this.scope
6900 cfg.valign=this.valign
6903 cfg.width=this.width
6922 * @class Roo.bootstrap.TableRow
6923 * @extends Roo.bootstrap.Component
6924 * Bootstrap TableRow class
6925 * @cfg {String} cls row class
6926 * @cfg {String} align Aligns the content in a table row
6927 * @cfg {String} bgcolor Specifies a background color for a table row
6928 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6929 * @cfg {String} valign Vertical aligns the content in a table row
6932 * Create a new TableRow
6933 * @param {Object} config The config object
6936 Roo.bootstrap.TableRow = function(config){
6937 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6940 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6948 getAutoCreate : function(){
6949 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6959 cfg.align = this.align;
6962 cfg.bgcolor = this.bgcolor;
6965 cfg.charoff = this.charoff;
6968 cfg.valign = this.valign;
6986 * @class Roo.bootstrap.TableBody
6987 * @extends Roo.bootstrap.Component
6988 * Bootstrap TableBody class
6989 * @cfg {String} cls element class
6990 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6991 * @cfg {String} align Aligns the content inside the element
6992 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6993 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6996 * Create a new TableBody
6997 * @param {Object} config The config object
7000 Roo.bootstrap.TableBody = function(config){
7001 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7004 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7012 getAutoCreate : function(){
7013 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7027 cfg.align = this.align;
7030 cfg.charoff = this.charoff;
7033 cfg.valign = this.valign;
7040 // initEvents : function()
7047 // this.store = Roo.factory(this.store, Roo.data);
7048 // this.store.on('load', this.onLoad, this);
7050 // this.store.load();
7054 // onLoad: function ()
7056 // this.fireEvent('load', this);
7066 * Ext JS Library 1.1.1
7067 * Copyright(c) 2006-2007, Ext JS, LLC.
7069 * Originally Released Under LGPL - original licence link has changed is not relivant.
7072 * <script type="text/javascript">
7075 // as we use this in bootstrap.
7076 Roo.namespace('Roo.form');
7078 * @class Roo.form.Action
7079 * Internal Class used to handle form actions
7081 * @param {Roo.form.BasicForm} el The form element or its id
7082 * @param {Object} config Configuration options
7087 // define the action interface
7088 Roo.form.Action = function(form, options){
7090 this.options = options || {};
7093 * Client Validation Failed
7096 Roo.form.Action.CLIENT_INVALID = 'client';
7098 * Server Validation Failed
7101 Roo.form.Action.SERVER_INVALID = 'server';
7103 * Connect to Server Failed
7106 Roo.form.Action.CONNECT_FAILURE = 'connect';
7108 * Reading Data from Server Failed
7111 Roo.form.Action.LOAD_FAILURE = 'load';
7113 Roo.form.Action.prototype = {
7115 failureType : undefined,
7116 response : undefined,
7120 run : function(options){
7125 success : function(response){
7130 handleResponse : function(response){
7134 // default connection failure
7135 failure : function(response){
7137 this.response = response;
7138 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7139 this.form.afterAction(this, false);
7142 processResponse : function(response){
7143 this.response = response;
7144 if(!response.responseText){
7147 this.result = this.handleResponse(response);
7151 // utility functions used internally
7152 getUrl : function(appendParams){
7153 var url = this.options.url || this.form.url || this.form.el.dom.action;
7155 var p = this.getParams();
7157 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7163 getMethod : function(){
7164 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7167 getParams : function(){
7168 var bp = this.form.baseParams;
7169 var p = this.options.params;
7171 if(typeof p == "object"){
7172 p = Roo.urlEncode(Roo.applyIf(p, bp));
7173 }else if(typeof p == 'string' && bp){
7174 p += '&' + Roo.urlEncode(bp);
7177 p = Roo.urlEncode(bp);
7182 createCallback : function(){
7184 success: this.success,
7185 failure: this.failure,
7187 timeout: (this.form.timeout*1000),
7188 upload: this.form.fileUpload ? this.success : undefined
7193 Roo.form.Action.Submit = function(form, options){
7194 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7197 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7200 haveProgress : false,
7201 uploadComplete : false,
7203 // uploadProgress indicator.
7204 uploadProgress : function()
7206 if (!this.form.progressUrl) {
7210 if (!this.haveProgress) {
7211 Roo.MessageBox.progress("Uploading", "Uploading");
7213 if (this.uploadComplete) {
7214 Roo.MessageBox.hide();
7218 this.haveProgress = true;
7220 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7222 var c = new Roo.data.Connection();
7224 url : this.form.progressUrl,
7229 success : function(req){
7230 //console.log(data);
7234 rdata = Roo.decode(req.responseText)
7236 Roo.log("Invalid data from server..");
7240 if (!rdata || !rdata.success) {
7242 Roo.MessageBox.alert(Roo.encode(rdata));
7245 var data = rdata.data;
7247 if (this.uploadComplete) {
7248 Roo.MessageBox.hide();
7253 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7254 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7257 this.uploadProgress.defer(2000,this);
7260 failure: function(data) {
7261 Roo.log('progress url failed ');
7272 // run get Values on the form, so it syncs any secondary forms.
7273 this.form.getValues();
7275 var o = this.options;
7276 var method = this.getMethod();
7277 var isPost = method == 'POST';
7278 if(o.clientValidation === false || this.form.isValid()){
7280 if (this.form.progressUrl) {
7281 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7282 (new Date() * 1) + '' + Math.random());
7287 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7288 form:this.form.el.dom,
7289 url:this.getUrl(!isPost),
7291 params:isPost ? this.getParams() : null,
7292 isUpload: this.form.fileUpload
7295 this.uploadProgress();
7297 }else if (o.clientValidation !== false){ // client validation failed
7298 this.failureType = Roo.form.Action.CLIENT_INVALID;
7299 this.form.afterAction(this, false);
7303 success : function(response)
7305 this.uploadComplete= true;
7306 if (this.haveProgress) {
7307 Roo.MessageBox.hide();
7311 var result = this.processResponse(response);
7312 if(result === true || result.success){
7313 this.form.afterAction(this, true);
7317 this.form.markInvalid(result.errors);
7318 this.failureType = Roo.form.Action.SERVER_INVALID;
7320 this.form.afterAction(this, false);
7322 failure : function(response)
7324 this.uploadComplete= true;
7325 if (this.haveProgress) {
7326 Roo.MessageBox.hide();
7329 this.response = response;
7330 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7331 this.form.afterAction(this, false);
7334 handleResponse : function(response){
7335 if(this.form.errorReader){
7336 var rs = this.form.errorReader.read(response);
7339 for(var i = 0, len = rs.records.length; i < len; i++) {
7340 var r = rs.records[i];
7344 if(errors.length < 1){
7348 success : rs.success,
7354 ret = Roo.decode(response.responseText);
7358 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7368 Roo.form.Action.Load = function(form, options){
7369 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7370 this.reader = this.form.reader;
7373 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7378 Roo.Ajax.request(Roo.apply(
7379 this.createCallback(), {
7380 method:this.getMethod(),
7381 url:this.getUrl(false),
7382 params:this.getParams()
7386 success : function(response){
7388 var result = this.processResponse(response);
7389 if(result === true || !result.success || !result.data){
7390 this.failureType = Roo.form.Action.LOAD_FAILURE;
7391 this.form.afterAction(this, false);
7394 this.form.clearInvalid();
7395 this.form.setValues(result.data);
7396 this.form.afterAction(this, true);
7399 handleResponse : function(response){
7400 if(this.form.reader){
7401 var rs = this.form.reader.read(response);
7402 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7404 success : rs.success,
7408 return Roo.decode(response.responseText);
7412 Roo.form.Action.ACTION_TYPES = {
7413 'load' : Roo.form.Action.Load,
7414 'submit' : Roo.form.Action.Submit
7423 * @class Roo.bootstrap.Form
7424 * @extends Roo.bootstrap.Component
7425 * Bootstrap Form class
7426 * @cfg {String} method GET | POST (default POST)
7427 * @cfg {String} labelAlign top | left (default top)
7428 * @cfg {String} align left | right - for navbars
7429 * @cfg {Boolean} loadMask load mask when submit (default true)
7434 * @param {Object} config The config object
7438 Roo.bootstrap.Form = function(config){
7439 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7442 * @event clientvalidation
7443 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7444 * @param {Form} this
7445 * @param {Boolean} valid true if the form has passed client-side validation
7447 clientvalidation: true,
7449 * @event beforeaction
7450 * Fires before any action is performed. Return false to cancel the action.
7451 * @param {Form} this
7452 * @param {Action} action The action to be performed
7456 * @event actionfailed
7457 * Fires when an action fails.
7458 * @param {Form} this
7459 * @param {Action} action The action that failed
7461 actionfailed : true,
7463 * @event actioncomplete
7464 * Fires when an action is completed.
7465 * @param {Form} this
7466 * @param {Action} action The action that completed
7468 actioncomplete : true
7473 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7476 * @cfg {String} method
7477 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7482 * The URL to use for form actions if one isn't supplied in the action options.
7485 * @cfg {Boolean} fileUpload
7486 * Set to true if this form is a file upload.
7490 * @cfg {Object} baseParams
7491 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7495 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7499 * @cfg {Sting} align (left|right) for navbar forms
7504 activeAction : null,
7507 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7508 * element by passing it or its id or mask the form itself by passing in true.
7511 waitMsgTarget : false,
7515 getAutoCreate : function(){
7519 method : this.method || 'POST',
7520 id : this.id || Roo.id(),
7523 if (this.parent().xtype.match(/^Nav/)) {
7524 cfg.cls = 'navbar-form navbar-' + this.align;
7528 if (this.labelAlign == 'left' ) {
7529 cfg.cls += ' form-horizontal';
7535 initEvents : function()
7537 this.el.on('submit', this.onSubmit, this);
7538 // this was added as random key presses on the form where triggering form submit.
7539 this.el.on('keypress', function(e) {
7540 if (e.getCharCode() != 13) {
7543 // we might need to allow it for textareas.. and some other items.
7544 // check e.getTarget().
7546 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7550 Roo.log("keypress blocked");
7558 onSubmit : function(e){
7563 * Returns true if client-side validation on the form is successful.
7566 isValid : function(){
7567 var items = this.getItems();
7569 items.each(function(f){
7578 * Returns true if any fields in this form have changed since their original load.
7581 isDirty : function(){
7583 var items = this.getItems();
7584 items.each(function(f){
7594 * Performs a predefined action (submit or load) or custom actions you define on this form.
7595 * @param {String} actionName The name of the action type
7596 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7597 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7598 * accept other config options):
7600 Property Type Description
7601 ---------------- --------------- ----------------------------------------------------------------------------------
7602 url String The url for the action (defaults to the form's url)
7603 method String The form method to use (defaults to the form's method, or POST if not defined)
7604 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7605 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7606 validate the form on the client (defaults to false)
7608 * @return {BasicForm} this
7610 doAction : function(action, options){
7611 if(typeof action == 'string'){
7612 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7614 if(this.fireEvent('beforeaction', this, action) !== false){
7615 this.beforeAction(action);
7616 action.run.defer(100, action);
7622 beforeAction : function(action){
7623 var o = action.options;
7626 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7628 // not really supported yet.. ??
7630 //if(this.waitMsgTarget === true){
7631 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7632 //}else if(this.waitMsgTarget){
7633 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7634 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7636 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7642 afterAction : function(action, success){
7643 this.activeAction = null;
7644 var o = action.options;
7646 //if(this.waitMsgTarget === true){
7648 //}else if(this.waitMsgTarget){
7649 // this.waitMsgTarget.unmask();
7651 // Roo.MessageBox.updateProgress(1);
7652 // Roo.MessageBox.hide();
7659 Roo.callback(o.success, o.scope, [this, action]);
7660 this.fireEvent('actioncomplete', this, action);
7664 // failure condition..
7665 // we have a scenario where updates need confirming.
7666 // eg. if a locking scenario exists..
7667 // we look for { errors : { needs_confirm : true }} in the response.
7669 (typeof(action.result) != 'undefined') &&
7670 (typeof(action.result.errors) != 'undefined') &&
7671 (typeof(action.result.errors.needs_confirm) != 'undefined')
7674 Roo.log("not supported yet");
7677 Roo.MessageBox.confirm(
7678 "Change requires confirmation",
7679 action.result.errorMsg,
7684 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7694 Roo.callback(o.failure, o.scope, [this, action]);
7695 // show an error message if no failed handler is set..
7696 if (!this.hasListener('actionfailed')) {
7697 Roo.log("need to add dialog support");
7699 Roo.MessageBox.alert("Error",
7700 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7701 action.result.errorMsg :
7702 "Saving Failed, please check your entries or try again"
7707 this.fireEvent('actionfailed', this, action);
7712 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7713 * @param {String} id The value to search for
7716 findField : function(id){
7717 var items = this.getItems();
7718 var field = items.get(id);
7720 items.each(function(f){
7721 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7728 return field || null;
7731 * Mark fields in this form invalid in bulk.
7732 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7733 * @return {BasicForm} this
7735 markInvalid : function(errors){
7736 if(errors instanceof Array){
7737 for(var i = 0, len = errors.length; i < len; i++){
7738 var fieldError = errors[i];
7739 var f = this.findField(fieldError.id);
7741 f.markInvalid(fieldError.msg);
7747 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7748 field.markInvalid(errors[id]);
7752 //Roo.each(this.childForms || [], function (f) {
7753 // f.markInvalid(errors);
7760 * Set values for fields in this form in bulk.
7761 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7762 * @return {BasicForm} this
7764 setValues : function(values){
7765 if(values instanceof Array){ // array of objects
7766 for(var i = 0, len = values.length; i < len; i++){
7768 var f = this.findField(v.id);
7770 f.setValue(v.value);
7771 if(this.trackResetOnLoad){
7772 f.originalValue = f.getValue();
7776 }else{ // object hash
7779 if(typeof values[id] != 'function' && (field = this.findField(id))){
7781 if (field.setFromData &&
7783 field.displayField &&
7784 // combos' with local stores can
7785 // be queried via setValue()
7786 // to set their value..
7787 (field.store && !field.store.isLocal)
7791 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7792 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7793 field.setFromData(sd);
7796 field.setValue(values[id]);
7800 if(this.trackResetOnLoad){
7801 field.originalValue = field.getValue();
7807 //Roo.each(this.childForms || [], function (f) {
7808 // f.setValues(values);
7815 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7816 * they are returned as an array.
7817 * @param {Boolean} asString
7820 getValues : function(asString){
7821 //if (this.childForms) {
7822 // copy values from the child forms
7823 // Roo.each(this.childForms, function (f) {
7824 // this.setValues(f.getValues());
7830 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7831 if(asString === true){
7834 return Roo.urlDecode(fs);
7838 * Returns the fields in this form as an object with key/value pairs.
7839 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7842 getFieldValues : function(with_hidden)
7844 var items = this.getItems();
7846 items.each(function(f){
7850 var v = f.getValue();
7851 if (f.inputType =='radio') {
7852 if (typeof(ret[f.getName()]) == 'undefined') {
7853 ret[f.getName()] = ''; // empty..
7856 if (!f.el.dom.checked) {
7864 // not sure if this supported any more..
7865 if ((typeof(v) == 'object') && f.getRawValue) {
7866 v = f.getRawValue() ; // dates..
7868 // combo boxes where name != hiddenName...
7869 if (f.name != f.getName()) {
7870 ret[f.name] = f.getRawValue();
7872 ret[f.getName()] = v;
7879 * Clears all invalid messages in this form.
7880 * @return {BasicForm} this
7882 clearInvalid : function(){
7883 var items = this.getItems();
7885 items.each(function(f){
7896 * @return {BasicForm} this
7899 var items = this.getItems();
7900 items.each(function(f){
7904 Roo.each(this.childForms || [], function (f) {
7911 getItems : function()
7913 var r=new Roo.util.MixedCollection(false, function(o){
7914 return o.id || (o.id = Roo.id());
7916 var iter = function(el) {
7923 Roo.each(el.items,function(e) {
7941 * Ext JS Library 1.1.1
7942 * Copyright(c) 2006-2007, Ext JS, LLC.
7944 * Originally Released Under LGPL - original licence link has changed is not relivant.
7947 * <script type="text/javascript">
7950 * @class Roo.form.VTypes
7951 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7954 Roo.form.VTypes = function(){
7955 // closure these in so they are only created once.
7956 var alpha = /^[a-zA-Z_]+$/;
7957 var alphanum = /^[a-zA-Z0-9_]+$/;
7958 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7959 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7961 // All these messages and functions are configurable
7964 * The function used to validate email addresses
7965 * @param {String} value The email address
7967 'email' : function(v){
7968 return email.test(v);
7971 * The error text to display when the email validation function returns false
7974 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7976 * The keystroke filter mask to be applied on email input
7979 'emailMask' : /[a-z0-9_\.\-@]/i,
7982 * The function used to validate URLs
7983 * @param {String} value The URL
7985 'url' : function(v){
7989 * The error text to display when the url validation function returns false
7992 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7995 * The function used to validate alpha values
7996 * @param {String} value The value
7998 'alpha' : function(v){
7999 return alpha.test(v);
8002 * The error text to display when the alpha validation function returns false
8005 'alphaText' : 'This field should only contain letters and _',
8007 * The keystroke filter mask to be applied on alpha input
8010 'alphaMask' : /[a-z_]/i,
8013 * The function used to validate alphanumeric values
8014 * @param {String} value The value
8016 'alphanum' : function(v){
8017 return alphanum.test(v);
8020 * The error text to display when the alphanumeric validation function returns false
8023 'alphanumText' : 'This field should only contain letters, numbers and _',
8025 * The keystroke filter mask to be applied on alphanumeric input
8028 'alphanumMask' : /[a-z0-9_]/i
8038 * @class Roo.bootstrap.Input
8039 * @extends Roo.bootstrap.Component
8040 * Bootstrap Input class
8041 * @cfg {Boolean} disabled is it disabled
8042 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8043 * @cfg {String} name name of the input
8044 * @cfg {string} fieldLabel - the label associated
8045 * @cfg {string} placeholder - placeholder to put in text.
8046 * @cfg {string} before - input group add on before
8047 * @cfg {string} after - input group add on after
8048 * @cfg {string} size - (lg|sm) or leave empty..
8049 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8050 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8051 * @cfg {Number} md colspan out of 12 for computer-sized screens
8052 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8053 * @cfg {string} value default value of the input
8054 * @cfg {Number} labelWidth set the width of label (0-12)
8055 * @cfg {String} labelAlign (top|left)
8056 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8057 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8058 * @cfg {String} indicatorpos (left|right) default left
8060 * @cfg {String} align (left|center|right) Default left
8061 * @cfg {Boolean} forceFeedback (true|false) Default false
8067 * Create a new Input
8068 * @param {Object} config The config object
8071 Roo.bootstrap.Input = function(config){
8072 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8077 * Fires when this field receives input focus.
8078 * @param {Roo.form.Field} this
8083 * Fires when this field loses input focus.
8084 * @param {Roo.form.Field} this
8089 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8090 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8091 * @param {Roo.form.Field} this
8092 * @param {Roo.EventObject} e The event object
8097 * Fires just before the field blurs if the field value has changed.
8098 * @param {Roo.form.Field} this
8099 * @param {Mixed} newValue The new value
8100 * @param {Mixed} oldValue The original value
8105 * Fires after the field has been marked as invalid.
8106 * @param {Roo.form.Field} this
8107 * @param {String} msg The validation message
8112 * Fires after the field has been validated with no errors.
8113 * @param {Roo.form.Field} this
8118 * Fires after the key up
8119 * @param {Roo.form.Field} this
8120 * @param {Roo.EventObject} e The event Object
8126 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8128 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8129 automatic validation (defaults to "keyup").
8131 validationEvent : "keyup",
8133 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8135 validateOnBlur : true,
8137 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8139 validationDelay : 250,
8141 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8143 focusClass : "x-form-focus", // not needed???
8147 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8149 invalidClass : "has-warning",
8152 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8154 validClass : "has-success",
8157 * @cfg {Boolean} hasFeedback (true|false) default true
8162 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8164 invalidFeedbackClass : "glyphicon-warning-sign",
8167 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8169 validFeedbackClass : "glyphicon-ok",
8172 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8174 selectOnFocus : false,
8177 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8181 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8186 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8188 disableKeyFilter : false,
8191 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8195 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8199 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8201 blankText : "This field is required",
8204 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8208 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8210 maxLength : Number.MAX_VALUE,
8212 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8214 minLengthText : "The minimum length for this field is {0}",
8216 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8218 maxLengthText : "The maximum length for this field is {0}",
8222 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8223 * If available, this function will be called only after the basic validators all return true, and will be passed the
8224 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8228 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8229 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8230 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8234 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8238 autocomplete: false,
8257 formatedValue : false,
8258 forceFeedback : false,
8260 indicatorpos : 'left',
8262 parentLabelAlign : function()
8265 while (parent.parent()) {
8266 parent = parent.parent();
8267 if (typeof(parent.labelAlign) !='undefined') {
8268 return parent.labelAlign;
8275 getAutoCreate : function()
8277 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8283 if(this.inputType != 'hidden'){
8284 cfg.cls = 'form-group' //input-group
8290 type : this.inputType,
8292 cls : 'form-control',
8293 placeholder : this.placeholder || '',
8294 autocomplete : this.autocomplete || 'new-password'
8298 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8301 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8302 input.maxLength = this.maxLength;
8305 if (this.disabled) {
8306 input.disabled=true;
8309 if (this.readOnly) {
8310 input.readonly=true;
8314 input.name = this.name;
8318 input.cls += ' input-' + this.size;
8322 ['xs','sm','md','lg'].map(function(size){
8323 if (settings[size]) {
8324 cfg.cls += ' col-' + size + '-' + settings[size];
8328 var inputblock = input;
8332 cls: 'glyphicon form-control-feedback'
8335 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8338 cls : 'has-feedback',
8346 if (this.before || this.after) {
8349 cls : 'input-group',
8353 if (this.before && typeof(this.before) == 'string') {
8355 inputblock.cn.push({
8357 cls : 'roo-input-before input-group-addon',
8361 if (this.before && typeof(this.before) == 'object') {
8362 this.before = Roo.factory(this.before);
8364 inputblock.cn.push({
8366 cls : 'roo-input-before input-group-' +
8367 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8371 inputblock.cn.push(input);
8373 if (this.after && typeof(this.after) == 'string') {
8374 inputblock.cn.push({
8376 cls : 'roo-input-after input-group-addon',
8380 if (this.after && typeof(this.after) == 'object') {
8381 this.after = Roo.factory(this.after);
8383 inputblock.cn.push({
8385 cls : 'roo-input-after input-group-' +
8386 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8390 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8391 inputblock.cls += ' has-feedback';
8392 inputblock.cn.push(feedback);
8396 if (align ==='left' && this.fieldLabel.length) {
8401 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8402 tooltip : 'This field is required'
8407 cls : 'control-label col-sm-' + this.labelWidth,
8408 html : this.fieldLabel
8412 cls : "col-sm-" + (12 - this.labelWidth),
8420 if(this.indicatorpos == 'right'){
8425 cls : 'control-label col-sm-' + this.labelWidth,
8426 html : this.fieldLabel
8431 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8432 tooltip : 'This field is required'
8435 cls : "col-sm-" + (12 - this.labelWidth),
8444 } else if ( this.fieldLabel.length) {
8449 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8450 tooltip : 'This field is required'
8454 //cls : 'input-group-addon',
8455 html : this.fieldLabel
8463 if(this.indicatorpos == 'right'){
8468 //cls : 'input-group-addon',
8469 html : this.fieldLabel
8474 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8475 tooltip : 'This field is required'
8495 if (this.parentType === 'Navbar' && this.parent().bar) {
8496 cfg.cls += ' navbar-form';
8499 if (this.parentType === 'NavGroup') {
8500 cfg.cls += ' navbar-form';
8508 * return the real input element.
8510 inputEl: function ()
8512 return this.el.select('input.form-control',true).first();
8515 tooltipEl : function()
8517 return this.inputEl();
8520 indicatorEl : function()
8522 var indicator = this.el.select('i.roo-required-indicator',true).first();
8532 setDisabled : function(v)
8534 var i = this.inputEl().dom;
8536 i.removeAttribute('disabled');
8540 i.setAttribute('disabled','true');
8542 initEvents : function()
8545 this.inputEl().on("keydown" , this.fireKey, this);
8546 this.inputEl().on("focus", this.onFocus, this);
8547 this.inputEl().on("blur", this.onBlur, this);
8549 this.inputEl().relayEvent('keyup', this);
8551 this.indicator = this.indicatorEl();
8554 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8555 this.indicator.hide();
8558 // reference to original value for reset
8559 this.originalValue = this.getValue();
8560 //Roo.form.TextField.superclass.initEvents.call(this);
8561 if(this.validationEvent == 'keyup'){
8562 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8563 this.inputEl().on('keyup', this.filterValidation, this);
8565 else if(this.validationEvent !== false){
8566 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8569 if(this.selectOnFocus){
8570 this.on("focus", this.preFocus, this);
8573 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8574 this.inputEl().on("keypress", this.filterKeys, this);
8576 this.inputEl().relayEvent('keypress', this);
8579 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8580 this.el.on("click", this.autoSize, this);
8583 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8584 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8587 if (typeof(this.before) == 'object') {
8588 this.before.render(this.el.select('.roo-input-before',true).first());
8590 if (typeof(this.after) == 'object') {
8591 this.after.render(this.el.select('.roo-input-after',true).first());
8596 filterValidation : function(e){
8597 if(!e.isNavKeyPress()){
8598 this.validationTask.delay(this.validationDelay);
8602 * Validates the field value
8603 * @return {Boolean} True if the value is valid, else false
8605 validate : function(){
8606 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8607 if(this.disabled || this.validateValue(this.getRawValue())){
8618 * Validates a value according to the field's validation rules and marks the field as invalid
8619 * if the validation fails
8620 * @param {Mixed} value The value to validate
8621 * @return {Boolean} True if the value is valid, else false
8623 validateValue : function(value){
8624 if(value.length < 1) { // if it's blank
8625 if(this.allowBlank){
8631 if(value.length < this.minLength){
8634 if(value.length > this.maxLength){
8638 var vt = Roo.form.VTypes;
8639 if(!vt[this.vtype](value, this)){
8643 if(typeof this.validator == "function"){
8644 var msg = this.validator(value);
8650 if(this.regex && !this.regex.test(value)){
8660 fireKey : function(e){
8661 //Roo.log('field ' + e.getKey());
8662 if(e.isNavKeyPress()){
8663 this.fireEvent("specialkey", this, e);
8666 focus : function (selectText){
8668 this.inputEl().focus();
8669 if(selectText === true){
8670 this.inputEl().dom.select();
8676 onFocus : function(){
8677 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8678 // this.el.addClass(this.focusClass);
8681 this.hasFocus = true;
8682 this.startValue = this.getValue();
8683 this.fireEvent("focus", this);
8687 beforeBlur : Roo.emptyFn,
8691 onBlur : function(){
8693 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8694 //this.el.removeClass(this.focusClass);
8696 this.hasFocus = false;
8697 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8700 var v = this.getValue();
8701 if(String(v) !== String(this.startValue)){
8702 this.fireEvent('change', this, v, this.startValue);
8704 this.fireEvent("blur", this);
8708 * Resets the current field value to the originally loaded value and clears any validation messages
8711 this.setValue(this.originalValue);
8715 * Returns the name of the field
8716 * @return {Mixed} name The name field
8718 getName: function(){
8722 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8723 * @return {Mixed} value The field value
8725 getValue : function(){
8727 var v = this.inputEl().getValue();
8732 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8733 * @return {Mixed} value The field value
8735 getRawValue : function(){
8736 var v = this.inputEl().getValue();
8742 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8743 * @param {Mixed} value The value to set
8745 setRawValue : function(v){
8746 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8749 selectText : function(start, end){
8750 var v = this.getRawValue();
8752 start = start === undefined ? 0 : start;
8753 end = end === undefined ? v.length : end;
8754 var d = this.inputEl().dom;
8755 if(d.setSelectionRange){
8756 d.setSelectionRange(start, end);
8757 }else if(d.createTextRange){
8758 var range = d.createTextRange();
8759 range.moveStart("character", start);
8760 range.moveEnd("character", v.length-end);
8767 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8768 * @param {Mixed} value The value to set
8770 setValue : function(v){
8773 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8779 processValue : function(value){
8780 if(this.stripCharsRe){
8781 var newValue = value.replace(this.stripCharsRe, '');
8782 if(newValue !== value){
8783 this.setRawValue(newValue);
8790 preFocus : function(){
8792 if(this.selectOnFocus){
8793 this.inputEl().dom.select();
8796 filterKeys : function(e){
8798 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8801 var c = e.getCharCode(), cc = String.fromCharCode(c);
8802 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8805 if(!this.maskRe.test(cc)){
8810 * Clear any invalid styles/messages for this field
8812 clearInvalid : function(){
8814 if(!this.el || this.preventMark){ // not rendered
8819 this.indicator.hide();
8822 this.el.removeClass(this.invalidClass);
8824 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8826 var feedback = this.el.select('.form-control-feedback', true).first();
8829 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8834 this.fireEvent('valid', this);
8838 * Mark this field as valid
8840 markValid : function()
8842 if(!this.el || this.preventMark){ // not rendered
8846 this.el.removeClass([this.invalidClass, this.validClass]);
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]);
8858 if(this.allowBlank && !this.getRawValue().length){
8863 this.indicator.hide();
8866 this.el.addClass(this.validClass);
8868 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8870 var feedback = this.el.select('.form-control-feedback', true).first();
8873 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8874 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8879 this.fireEvent('valid', this);
8883 * Mark this field as invalid
8884 * @param {String} msg The validation message
8886 markInvalid : function(msg)
8888 if(!this.el || this.preventMark){ // not rendered
8892 this.el.removeClass([this.invalidClass, this.validClass]);
8894 var feedback = this.el.select('.form-control-feedback', true).first();
8897 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8904 if(this.allowBlank && !this.getRawValue().length){
8909 this.indicator.show();
8912 this.el.addClass(this.invalidClass);
8914 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8916 var feedback = this.el.select('.form-control-feedback', true).first();
8919 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8921 if(this.getValue().length || this.forceFeedback){
8922 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8929 this.fireEvent('invalid', this, msg);
8932 SafariOnKeyDown : function(event)
8934 // this is a workaround for a password hang bug on chrome/ webkit.
8935 if (this.inputEl().dom.type != 'password') {
8939 var isSelectAll = false;
8941 if(this.inputEl().dom.selectionEnd > 0){
8942 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8944 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8945 event.preventDefault();
8950 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
8952 event.preventDefault();
8953 // this is very hacky as keydown always get's upper case.
8955 var cc = String.fromCharCode(event.getCharCode());
8956 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8960 adjustWidth : function(tag, w){
8961 tag = tag.toLowerCase();
8962 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8963 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8967 if(tag == 'textarea'){
8970 }else if(Roo.isOpera){
8974 if(tag == 'textarea'){
8993 * @class Roo.bootstrap.TextArea
8994 * @extends Roo.bootstrap.Input
8995 * Bootstrap TextArea class
8996 * @cfg {Number} cols Specifies the visible width of a text area
8997 * @cfg {Number} rows Specifies the visible number of lines in a text area
8998 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8999 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9000 * @cfg {string} html text
9003 * Create a new TextArea
9004 * @param {Object} config The config object
9007 Roo.bootstrap.TextArea = function(config){
9008 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9012 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9022 getAutoCreate : function(){
9024 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9035 value : this.value || '',
9036 html: this.html || '',
9037 cls : 'form-control',
9038 placeholder : this.placeholder || ''
9042 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9043 input.maxLength = this.maxLength;
9047 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9051 input.cols = this.cols;
9054 if (this.readOnly) {
9055 input.readonly = true;
9059 input.name = this.name;
9063 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9067 ['xs','sm','md','lg'].map(function(size){
9068 if (settings[size]) {
9069 cfg.cls += ' col-' + size + '-' + settings[size];
9073 var inputblock = input;
9075 if(this.hasFeedback && !this.allowBlank){
9079 cls: 'glyphicon form-control-feedback'
9083 cls : 'has-feedback',
9092 if (this.before || this.after) {
9095 cls : 'input-group',
9099 inputblock.cn.push({
9101 cls : 'input-group-addon',
9106 inputblock.cn.push(input);
9108 if(this.hasFeedback && !this.allowBlank){
9109 inputblock.cls += ' has-feedback';
9110 inputblock.cn.push(feedback);
9114 inputblock.cn.push({
9116 cls : 'input-group-addon',
9123 if (align ==='left' && this.fieldLabel.length) {
9124 // Roo.log("left and has label");
9130 cls : 'control-label col-sm-' + this.labelWidth,
9131 html : this.fieldLabel
9135 cls : "col-sm-" + (12 - this.labelWidth),
9142 } else if ( this.fieldLabel.length) {
9143 // Roo.log(" label");
9148 //cls : 'input-group-addon',
9149 html : this.fieldLabel
9159 // Roo.log(" no label && no align");
9169 if (this.disabled) {
9170 input.disabled=true;
9177 * return the real textarea element.
9179 inputEl: function ()
9181 return this.el.select('textarea.form-control',true).first();
9185 * Clear any invalid styles/messages for this field
9187 clearInvalid : function()
9190 if(!this.el || this.preventMark){ // not rendered
9194 var label = this.el.select('label', true).first();
9195 var icon = this.el.select('i.fa-star', true).first();
9201 this.el.removeClass(this.invalidClass);
9203 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9205 var feedback = this.el.select('.form-control-feedback', true).first();
9208 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9213 this.fireEvent('valid', this);
9217 * Mark this field as valid
9219 markValid : function()
9221 if(!this.el || this.preventMark){ // not rendered
9225 this.el.removeClass([this.invalidClass, this.validClass]);
9227 var feedback = this.el.select('.form-control-feedback', true).first();
9230 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9233 if(this.disabled || this.allowBlank){
9237 var label = this.el.select('label', true).first();
9238 var icon = this.el.select('i.fa-star', true).first();
9244 this.el.addClass(this.validClass);
9246 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9248 var feedback = this.el.select('.form-control-feedback', true).first();
9251 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9252 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9257 this.fireEvent('valid', this);
9261 * Mark this field as invalid
9262 * @param {String} msg The validation message
9264 markInvalid : function(msg)
9266 if(!this.el || this.preventMark){ // not rendered
9270 this.el.removeClass([this.invalidClass, this.validClass]);
9272 var feedback = this.el.select('.form-control-feedback', true).first();
9275 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9278 if(this.disabled || this.allowBlank){
9282 var label = this.el.select('label', true).first();
9283 var icon = this.el.select('i.fa-star', true).first();
9285 if(!this.getValue().length && label && !icon){
9286 this.el.createChild({
9288 cls : 'text-danger fa fa-lg fa-star',
9289 tooltip : 'This field is required',
9290 style : 'margin-right:5px;'
9294 this.el.addClass(this.invalidClass);
9296 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9298 var feedback = this.el.select('.form-control-feedback', true).first();
9301 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9303 if(this.getValue().length || this.forceFeedback){
9304 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9311 this.fireEvent('invalid', this, msg);
9319 * trigger field - base class for combo..
9324 * @class Roo.bootstrap.TriggerField
9325 * @extends Roo.bootstrap.Input
9326 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9327 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9328 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9329 * for which you can provide a custom implementation. For example:
9331 var trigger = new Roo.bootstrap.TriggerField();
9332 trigger.onTriggerClick = myTriggerFn;
9333 trigger.applyTo('my-field');
9336 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9337 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9338 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9339 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9340 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9343 * Create a new TriggerField.
9344 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9345 * to the base TextField)
9347 Roo.bootstrap.TriggerField = function(config){
9348 this.mimicing = false;
9349 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9352 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9354 * @cfg {String} triggerClass A CSS class to apply to the trigger
9357 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9362 * @cfg {Boolean} removable (true|false) special filter default false
9366 /** @cfg {Boolean} grow @hide */
9367 /** @cfg {Number} growMin @hide */
9368 /** @cfg {Number} growMax @hide */
9374 autoSize: Roo.emptyFn,
9381 actionMode : 'wrap',
9386 getAutoCreate : function(){
9388 var align = this.labelAlign || this.parentLabelAlign();
9393 cls: 'form-group' //input-group
9400 type : this.inputType,
9401 cls : 'form-control',
9402 autocomplete: 'new-password',
9403 placeholder : this.placeholder || ''
9407 input.name = this.name;
9410 input.cls += ' input-' + this.size;
9413 if (this.disabled) {
9414 input.disabled=true;
9417 var inputblock = input;
9419 if(this.hasFeedback && !this.allowBlank){
9423 cls: 'glyphicon form-control-feedback'
9426 if(this.removable && !this.editable && !this.tickable){
9428 cls : 'has-feedback',
9434 cls : 'roo-combo-removable-btn close'
9441 cls : 'has-feedback',
9450 if(this.removable && !this.editable && !this.tickable){
9452 cls : 'roo-removable',
9458 cls : 'roo-combo-removable-btn close'
9465 if (this.before || this.after) {
9468 cls : 'input-group',
9472 inputblock.cn.push({
9474 cls : 'input-group-addon',
9479 inputblock.cn.push(input);
9481 if(this.hasFeedback && !this.allowBlank){
9482 inputblock.cls += ' has-feedback';
9483 inputblock.cn.push(feedback);
9487 inputblock.cn.push({
9489 cls : 'input-group-addon',
9502 cls: 'form-hidden-field'
9516 cls: 'form-hidden-field'
9520 cls: 'roo-select2-choices',
9524 cls: 'roo-select2-search-field',
9537 cls: 'roo-select2-container input-group',
9542 // cls: 'typeahead typeahead-long dropdown-menu',
9543 // style: 'display:none'
9548 if(!this.multiple && this.showToggleBtn){
9554 if (this.caret != false) {
9557 cls: 'fa fa-' + this.caret
9564 cls : 'input-group-addon btn dropdown-toggle',
9569 cls: 'combobox-clear',
9583 combobox.cls += ' roo-select2-container-multi';
9586 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9588 // Roo.log("left and has label");
9592 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9593 tooltip : 'This field is required'
9598 cls : 'control-label col-sm-' + this.labelWidth,
9599 html : this.fieldLabel
9603 cls : "col-sm-" + (12 - this.labelWidth),
9611 if(this.indicatorpos == 'right'){
9616 cls : 'control-label col-sm-' + this.labelWidth,
9617 html : this.fieldLabel
9622 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9623 tooltip : 'This field is required'
9626 cls : "col-sm-" + (12 - this.labelWidth),
9635 } else if ( this.fieldLabel.length) {
9636 // Roo.log(" label");
9640 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9641 tooltip : 'This field is required'
9645 //cls : 'input-group-addon',
9646 html : this.fieldLabel
9654 if(this.indicatorpos == 'right'){
9659 //cls : 'input-group-addon',
9660 html : this.fieldLabel
9665 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9666 tooltip : 'This field is required'
9677 // Roo.log(" no label && no align");
9684 ['xs','sm','md','lg'].map(function(size){
9685 if (settings[size]) {
9686 cfg.cls += ' col-' + size + '-' + settings[size];
9697 onResize : function(w, h){
9698 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9699 // if(typeof w == 'number'){
9700 // var x = w - this.trigger.getWidth();
9701 // this.inputEl().setWidth(this.adjustWidth('input', x));
9702 // this.trigger.setStyle('left', x+'px');
9707 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9710 getResizeEl : function(){
9711 return this.inputEl();
9715 getPositionEl : function(){
9716 return this.inputEl();
9720 alignErrorIcon : function(){
9721 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9725 initEvents : function(){
9729 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9730 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9731 if(!this.multiple && this.showToggleBtn){
9732 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9733 if(this.hideTrigger){
9734 this.trigger.setDisplayed(false);
9736 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9740 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9743 if(this.removable && !this.editable && !this.tickable){
9744 var close = this.closeTriggerEl();
9747 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9748 close.on('click', this.removeBtnClick, this, close);
9752 //this.trigger.addClassOnOver('x-form-trigger-over');
9753 //this.trigger.addClassOnClick('x-form-trigger-click');
9756 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9760 closeTriggerEl : function()
9762 var close = this.el.select('.roo-combo-removable-btn', true).first();
9763 return close ? close : false;
9766 removeBtnClick : function(e, h, el)
9770 if(this.fireEvent("remove", this) !== false){
9772 this.fireEvent("afterremove", this)
9776 createList : function()
9778 this.list = Roo.get(document.body).createChild({
9780 cls: 'typeahead typeahead-long dropdown-menu',
9781 style: 'display:none'
9784 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9789 initTrigger : function(){
9794 onDestroy : function(){
9796 this.trigger.removeAllListeners();
9797 // this.trigger.remove();
9800 // this.wrap.remove();
9802 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9806 onFocus : function(){
9807 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9810 this.wrap.addClass('x-trigger-wrap-focus');
9811 this.mimicing = true;
9812 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9813 if(this.monitorTab){
9814 this.el.on("keydown", this.checkTab, this);
9821 checkTab : function(e){
9822 if(e.getKey() == e.TAB){
9828 onBlur : function(){
9833 mimicBlur : function(e, t){
9835 if(!this.wrap.contains(t) && this.validateBlur()){
9842 triggerBlur : function(){
9843 this.mimicing = false;
9844 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9845 if(this.monitorTab){
9846 this.el.un("keydown", this.checkTab, this);
9848 //this.wrap.removeClass('x-trigger-wrap-focus');
9849 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9853 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9854 validateBlur : function(e, t){
9859 onDisable : function(){
9860 this.inputEl().dom.disabled = true;
9861 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9863 // this.wrap.addClass('x-item-disabled');
9868 onEnable : function(){
9869 this.inputEl().dom.disabled = false;
9870 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9872 // this.el.removeClass('x-item-disabled');
9877 onShow : function(){
9878 var ae = this.getActionEl();
9881 ae.dom.style.display = '';
9882 ae.dom.style.visibility = 'visible';
9888 onHide : function(){
9889 var ae = this.getActionEl();
9890 ae.dom.style.display = 'none';
9894 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9895 * by an implementing function.
9897 * @param {EventObject} e
9899 onTriggerClick : Roo.emptyFn
9903 * Ext JS Library 1.1.1
9904 * Copyright(c) 2006-2007, Ext JS, LLC.
9906 * Originally Released Under LGPL - original licence link has changed is not relivant.
9909 * <script type="text/javascript">
9914 * @class Roo.data.SortTypes
9916 * Defines the default sorting (casting?) comparison functions used when sorting data.
9918 Roo.data.SortTypes = {
9920 * Default sort that does nothing
9921 * @param {Mixed} s The value being converted
9922 * @return {Mixed} The comparison value
9929 * The regular expression used to strip tags
9933 stripTagsRE : /<\/?[^>]+>/gi,
9936 * Strips all HTML tags to sort on text only
9937 * @param {Mixed} s The value being converted
9938 * @return {String} The comparison value
9940 asText : function(s){
9941 return String(s).replace(this.stripTagsRE, "");
9945 * Strips all HTML tags to sort on text only - Case insensitive
9946 * @param {Mixed} s The value being converted
9947 * @return {String} The comparison value
9949 asUCText : function(s){
9950 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9954 * Case insensitive string
9955 * @param {Mixed} s The value being converted
9956 * @return {String} The comparison value
9958 asUCString : function(s) {
9959 return String(s).toUpperCase();
9964 * @param {Mixed} s The value being converted
9965 * @return {Number} The comparison value
9967 asDate : function(s) {
9971 if(s instanceof Date){
9974 return Date.parse(String(s));
9979 * @param {Mixed} s The value being converted
9980 * @return {Float} The comparison value
9982 asFloat : function(s) {
9983 var val = parseFloat(String(s).replace(/,/g, ""));
9992 * @param {Mixed} s The value being converted
9993 * @return {Number} The comparison value
9995 asInt : function(s) {
9996 var val = parseInt(String(s).replace(/,/g, ""));
10004 * Ext JS Library 1.1.1
10005 * Copyright(c) 2006-2007, Ext JS, LLC.
10007 * Originally Released Under LGPL - original licence link has changed is not relivant.
10010 * <script type="text/javascript">
10014 * @class Roo.data.Record
10015 * Instances of this class encapsulate both record <em>definition</em> information, and record
10016 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10017 * to access Records cached in an {@link Roo.data.Store} object.<br>
10019 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10020 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10023 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10025 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10026 * {@link #create}. The parameters are the same.
10027 * @param {Array} data An associative Array of data values keyed by the field name.
10028 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10029 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10030 * not specified an integer id is generated.
10032 Roo.data.Record = function(data, id){
10033 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10038 * Generate a constructor for a specific record layout.
10039 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10040 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10041 * Each field definition object may contain the following properties: <ul>
10042 * <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,
10043 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10044 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10045 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10046 * is being used, then this is a string containing the javascript expression to reference the data relative to
10047 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10048 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10049 * this may be omitted.</p></li>
10050 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10051 * <ul><li>auto (Default, implies no conversion)</li>
10056 * <li>date</li></ul></p></li>
10057 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10058 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10059 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10060 * by the Reader into an object that will be stored in the Record. It is passed the
10061 * following parameters:<ul>
10062 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10064 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10066 * <br>usage:<br><pre><code>
10067 var TopicRecord = Roo.data.Record.create(
10068 {name: 'title', mapping: 'topic_title'},
10069 {name: 'author', mapping: 'username'},
10070 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10071 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10072 {name: 'lastPoster', mapping: 'user2'},
10073 {name: 'excerpt', mapping: 'post_text'}
10076 var myNewRecord = new TopicRecord({
10077 title: 'Do my job please',
10080 lastPost: new Date(),
10081 lastPoster: 'Animal',
10082 excerpt: 'No way dude!'
10084 myStore.add(myNewRecord);
10089 Roo.data.Record.create = function(o){
10090 var f = function(){
10091 f.superclass.constructor.apply(this, arguments);
10093 Roo.extend(f, Roo.data.Record);
10094 var p = f.prototype;
10095 p.fields = new Roo.util.MixedCollection(false, function(field){
10098 for(var i = 0, len = o.length; i < len; i++){
10099 p.fields.add(new Roo.data.Field(o[i]));
10101 f.getField = function(name){
10102 return p.fields.get(name);
10107 Roo.data.Record.AUTO_ID = 1000;
10108 Roo.data.Record.EDIT = 'edit';
10109 Roo.data.Record.REJECT = 'reject';
10110 Roo.data.Record.COMMIT = 'commit';
10112 Roo.data.Record.prototype = {
10114 * Readonly flag - true if this record has been modified.
10123 join : function(store){
10124 this.store = store;
10128 * Set the named field to the specified value.
10129 * @param {String} name The name of the field to set.
10130 * @param {Object} value The value to set the field to.
10132 set : function(name, value){
10133 if(this.data[name] == value){
10137 if(!this.modified){
10138 this.modified = {};
10140 if(typeof this.modified[name] == 'undefined'){
10141 this.modified[name] = this.data[name];
10143 this.data[name] = value;
10144 if(!this.editing && this.store){
10145 this.store.afterEdit(this);
10150 * Get the value of the named field.
10151 * @param {String} name The name of the field to get the value of.
10152 * @return {Object} The value of the field.
10154 get : function(name){
10155 return this.data[name];
10159 beginEdit : function(){
10160 this.editing = true;
10161 this.modified = {};
10165 cancelEdit : function(){
10166 this.editing = false;
10167 delete this.modified;
10171 endEdit : function(){
10172 this.editing = false;
10173 if(this.dirty && this.store){
10174 this.store.afterEdit(this);
10179 * Usually called by the {@link Roo.data.Store} which owns the Record.
10180 * Rejects all changes made to the Record since either creation, or the last commit operation.
10181 * Modified fields are reverted to their original values.
10183 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10184 * of reject operations.
10186 reject : function(){
10187 var m = this.modified;
10189 if(typeof m[n] != "function"){
10190 this.data[n] = m[n];
10193 this.dirty = false;
10194 delete this.modified;
10195 this.editing = false;
10197 this.store.afterReject(this);
10202 * Usually called by the {@link Roo.data.Store} which owns the Record.
10203 * Commits all changes made to the Record since either creation, or the last commit operation.
10205 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10206 * of commit operations.
10208 commit : function(){
10209 this.dirty = false;
10210 delete this.modified;
10211 this.editing = false;
10213 this.store.afterCommit(this);
10218 hasError : function(){
10219 return this.error != null;
10223 clearError : function(){
10228 * Creates a copy of this record.
10229 * @param {String} id (optional) A new record id if you don't want to use this record's id
10232 copy : function(newId) {
10233 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10237 * Ext JS Library 1.1.1
10238 * Copyright(c) 2006-2007, Ext JS, LLC.
10240 * Originally Released Under LGPL - original licence link has changed is not relivant.
10243 * <script type="text/javascript">
10249 * @class Roo.data.Store
10250 * @extends Roo.util.Observable
10251 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10252 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10254 * 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
10255 * has no knowledge of the format of the data returned by the Proxy.<br>
10257 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10258 * instances from the data object. These records are cached and made available through accessor functions.
10260 * Creates a new Store.
10261 * @param {Object} config A config object containing the objects needed for the Store to access data,
10262 * and read the data into Records.
10264 Roo.data.Store = function(config){
10265 this.data = new Roo.util.MixedCollection(false);
10266 this.data.getKey = function(o){
10269 this.baseParams = {};
10271 this.paramNames = {
10276 "multisort" : "_multisort"
10279 if(config && config.data){
10280 this.inlineData = config.data;
10281 delete config.data;
10284 Roo.apply(this, config);
10286 if(this.reader){ // reader passed
10287 this.reader = Roo.factory(this.reader, Roo.data);
10288 this.reader.xmodule = this.xmodule || false;
10289 if(!this.recordType){
10290 this.recordType = this.reader.recordType;
10292 if(this.reader.onMetaChange){
10293 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10297 if(this.recordType){
10298 this.fields = this.recordType.prototype.fields;
10300 this.modified = [];
10304 * @event datachanged
10305 * Fires when the data cache has changed, and a widget which is using this Store
10306 * as a Record cache should refresh its view.
10307 * @param {Store} this
10309 datachanged : true,
10311 * @event metachange
10312 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10313 * @param {Store} this
10314 * @param {Object} meta The JSON metadata
10319 * Fires when Records have been added to the Store
10320 * @param {Store} this
10321 * @param {Roo.data.Record[]} records The array of Records added
10322 * @param {Number} index The index at which the record(s) were added
10327 * Fires when a Record has been removed from the Store
10328 * @param {Store} this
10329 * @param {Roo.data.Record} record The Record that was removed
10330 * @param {Number} index The index at which the record was removed
10335 * Fires when a Record has been updated
10336 * @param {Store} this
10337 * @param {Roo.data.Record} record The Record that was updated
10338 * @param {String} operation The update operation being performed. Value may be one of:
10340 Roo.data.Record.EDIT
10341 Roo.data.Record.REJECT
10342 Roo.data.Record.COMMIT
10348 * Fires when the data cache has been cleared.
10349 * @param {Store} this
10353 * @event beforeload
10354 * Fires before a request is made for a new data object. If the beforeload handler returns false
10355 * the load action will be canceled.
10356 * @param {Store} this
10357 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10361 * @event beforeloadadd
10362 * Fires after a new set of Records has been loaded.
10363 * @param {Store} this
10364 * @param {Roo.data.Record[]} records The Records that were loaded
10365 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10367 beforeloadadd : true,
10370 * Fires after a new set of Records has been loaded, before they are added to the store.
10371 * @param {Store} this
10372 * @param {Roo.data.Record[]} records The Records that were loaded
10373 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10374 * @params {Object} return from reader
10378 * @event loadexception
10379 * Fires if an exception occurs in the Proxy during loading.
10380 * Called with the signature of the Proxy's "loadexception" event.
10381 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10384 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10385 * @param {Object} load options
10386 * @param {Object} jsonData from your request (normally this contains the Exception)
10388 loadexception : true
10392 this.proxy = Roo.factory(this.proxy, Roo.data);
10393 this.proxy.xmodule = this.xmodule || false;
10394 this.relayEvents(this.proxy, ["loadexception"]);
10396 this.sortToggle = {};
10397 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10399 Roo.data.Store.superclass.constructor.call(this);
10401 if(this.inlineData){
10402 this.loadData(this.inlineData);
10403 delete this.inlineData;
10407 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10409 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10410 * without a remote query - used by combo/forms at present.
10414 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10417 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10420 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10421 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10424 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10425 * on any HTTP request
10428 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10431 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10435 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10436 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10438 remoteSort : false,
10441 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10442 * loaded or when a record is removed. (defaults to false).
10444 pruneModifiedRecords : false,
10447 lastOptions : null,
10450 * Add Records to the Store and fires the add event.
10451 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10453 add : function(records){
10454 records = [].concat(records);
10455 for(var i = 0, len = records.length; i < len; i++){
10456 records[i].join(this);
10458 var index = this.data.length;
10459 this.data.addAll(records);
10460 this.fireEvent("add", this, records, index);
10464 * Remove a Record from the Store and fires the remove event.
10465 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10467 remove : function(record){
10468 var index = this.data.indexOf(record);
10469 this.data.removeAt(index);
10470 if(this.pruneModifiedRecords){
10471 this.modified.remove(record);
10473 this.fireEvent("remove", this, record, index);
10477 * Remove all Records from the Store and fires the clear event.
10479 removeAll : function(){
10481 if(this.pruneModifiedRecords){
10482 this.modified = [];
10484 this.fireEvent("clear", this);
10488 * Inserts Records to the Store at the given index and fires the add event.
10489 * @param {Number} index The start index at which to insert the passed Records.
10490 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10492 insert : function(index, records){
10493 records = [].concat(records);
10494 for(var i = 0, len = records.length; i < len; i++){
10495 this.data.insert(index, records[i]);
10496 records[i].join(this);
10498 this.fireEvent("add", this, records, index);
10502 * Get the index within the cache of the passed Record.
10503 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10504 * @return {Number} The index of the passed Record. Returns -1 if not found.
10506 indexOf : function(record){
10507 return this.data.indexOf(record);
10511 * Get the index within the cache of the Record with the passed id.
10512 * @param {String} id The id of the Record to find.
10513 * @return {Number} The index of the Record. Returns -1 if not found.
10515 indexOfId : function(id){
10516 return this.data.indexOfKey(id);
10520 * Get the Record with the specified id.
10521 * @param {String} id The id of the Record to find.
10522 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10524 getById : function(id){
10525 return this.data.key(id);
10529 * Get the Record at the specified index.
10530 * @param {Number} index The index of the Record to find.
10531 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10533 getAt : function(index){
10534 return this.data.itemAt(index);
10538 * Returns a range of Records between specified indices.
10539 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10540 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10541 * @return {Roo.data.Record[]} An array of Records
10543 getRange : function(start, end){
10544 return this.data.getRange(start, end);
10548 storeOptions : function(o){
10549 o = Roo.apply({}, o);
10552 this.lastOptions = o;
10556 * Loads the Record cache from the configured Proxy using the configured Reader.
10558 * If using remote paging, then the first load call must specify the <em>start</em>
10559 * and <em>limit</em> properties in the options.params property to establish the initial
10560 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10562 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10563 * and this call will return before the new data has been loaded. Perform any post-processing
10564 * in a callback function, or in a "load" event handler.</strong>
10566 * @param {Object} options An object containing properties which control loading options:<ul>
10567 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10568 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10569 * passed the following arguments:<ul>
10570 * <li>r : Roo.data.Record[]</li>
10571 * <li>options: Options object from the load call</li>
10572 * <li>success: Boolean success indicator</li></ul></li>
10573 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10574 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10577 load : function(options){
10578 options = options || {};
10579 if(this.fireEvent("beforeload", this, options) !== false){
10580 this.storeOptions(options);
10581 var p = Roo.apply(options.params || {}, this.baseParams);
10582 // if meta was not loaded from remote source.. try requesting it.
10583 if (!this.reader.metaFromRemote) {
10584 p._requestMeta = 1;
10586 if(this.sortInfo && this.remoteSort){
10587 var pn = this.paramNames;
10588 p[pn["sort"]] = this.sortInfo.field;
10589 p[pn["dir"]] = this.sortInfo.direction;
10591 if (this.multiSort) {
10592 var pn = this.paramNames;
10593 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10596 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10601 * Reloads the Record cache from the configured Proxy using the configured Reader and
10602 * the options from the last load operation performed.
10603 * @param {Object} options (optional) An object containing properties which may override the options
10604 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10605 * the most recently used options are reused).
10607 reload : function(options){
10608 this.load(Roo.applyIf(options||{}, this.lastOptions));
10612 // Called as a callback by the Reader during a load operation.
10613 loadRecords : function(o, options, success){
10614 if(!o || success === false){
10615 if(success !== false){
10616 this.fireEvent("load", this, [], options, o);
10618 if(options.callback){
10619 options.callback.call(options.scope || this, [], options, false);
10623 // if data returned failure - throw an exception.
10624 if (o.success === false) {
10625 // show a message if no listener is registered.
10626 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10627 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10629 // loadmask wil be hooked into this..
10630 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10633 var r = o.records, t = o.totalRecords || r.length;
10635 this.fireEvent("beforeloadadd", this, r, options, o);
10637 if(!options || options.add !== true){
10638 if(this.pruneModifiedRecords){
10639 this.modified = [];
10641 for(var i = 0, len = r.length; i < len; i++){
10645 this.data = this.snapshot;
10646 delete this.snapshot;
10649 this.data.addAll(r);
10650 this.totalLength = t;
10652 this.fireEvent("datachanged", this);
10654 this.totalLength = Math.max(t, this.data.length+r.length);
10657 this.fireEvent("load", this, r, options, o);
10658 if(options.callback){
10659 options.callback.call(options.scope || this, r, options, true);
10665 * Loads data from a passed data block. A Reader which understands the format of the data
10666 * must have been configured in the constructor.
10667 * @param {Object} data The data block from which to read the Records. The format of the data expected
10668 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10669 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10671 loadData : function(o, append){
10672 var r = this.reader.readRecords(o);
10673 this.loadRecords(r, {add: append}, true);
10677 * Gets the number of cached records.
10679 * <em>If using paging, this may not be the total size of the dataset. If the data object
10680 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10681 * the data set size</em>
10683 getCount : function(){
10684 return this.data.length || 0;
10688 * Gets the total number of records in the dataset as returned by the server.
10690 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10691 * the dataset size</em>
10693 getTotalCount : function(){
10694 return this.totalLength || 0;
10698 * Returns the sort state of the Store as an object with two properties:
10700 field {String} The name of the field by which the Records are sorted
10701 direction {String} The sort order, "ASC" or "DESC"
10704 getSortState : function(){
10705 return this.sortInfo;
10709 applySort : function(){
10710 if(this.sortInfo && !this.remoteSort){
10711 var s = this.sortInfo, f = s.field;
10712 var st = this.fields.get(f).sortType;
10713 var fn = function(r1, r2){
10714 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10715 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10717 this.data.sort(s.direction, fn);
10718 if(this.snapshot && this.snapshot != this.data){
10719 this.snapshot.sort(s.direction, fn);
10725 * Sets the default sort column and order to be used by the next load operation.
10726 * @param {String} fieldName The name of the field to sort by.
10727 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10729 setDefaultSort : function(field, dir){
10730 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10734 * Sort the Records.
10735 * If remote sorting is used, the sort is performed on the server, and the cache is
10736 * reloaded. If local sorting is used, the cache is sorted internally.
10737 * @param {String} fieldName The name of the field to sort by.
10738 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10740 sort : function(fieldName, dir){
10741 var f = this.fields.get(fieldName);
10743 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10745 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10746 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10751 this.sortToggle[f.name] = dir;
10752 this.sortInfo = {field: f.name, direction: dir};
10753 if(!this.remoteSort){
10755 this.fireEvent("datachanged", this);
10757 this.load(this.lastOptions);
10762 * Calls the specified function for each of the Records in the cache.
10763 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10764 * Returning <em>false</em> aborts and exits the iteration.
10765 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10767 each : function(fn, scope){
10768 this.data.each(fn, scope);
10772 * Gets all records modified since the last commit. Modified records are persisted across load operations
10773 * (e.g., during paging).
10774 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10776 getModifiedRecords : function(){
10777 return this.modified;
10781 createFilterFn : function(property, value, anyMatch){
10782 if(!value.exec){ // not a regex
10783 value = String(value);
10784 if(value.length == 0){
10787 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10789 return function(r){
10790 return value.test(r.data[property]);
10795 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10796 * @param {String} property A field on your records
10797 * @param {Number} start The record index to start at (defaults to 0)
10798 * @param {Number} end The last record index to include (defaults to length - 1)
10799 * @return {Number} The sum
10801 sum : function(property, start, end){
10802 var rs = this.data.items, v = 0;
10803 start = start || 0;
10804 end = (end || end === 0) ? end : rs.length-1;
10806 for(var i = start; i <= end; i++){
10807 v += (rs[i].data[property] || 0);
10813 * Filter the records by a specified property.
10814 * @param {String} field A field on your records
10815 * @param {String/RegExp} value Either a string that the field
10816 * should start with or a RegExp to test against the field
10817 * @param {Boolean} anyMatch True to match any part not just the beginning
10819 filter : function(property, value, anyMatch){
10820 var fn = this.createFilterFn(property, value, anyMatch);
10821 return fn ? this.filterBy(fn) : this.clearFilter();
10825 * Filter by a function. The specified function will be called with each
10826 * record in this data source. If the function returns true the record is included,
10827 * otherwise it is filtered.
10828 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10829 * @param {Object} scope (optional) The scope of the function (defaults to this)
10831 filterBy : function(fn, scope){
10832 this.snapshot = this.snapshot || this.data;
10833 this.data = this.queryBy(fn, scope||this);
10834 this.fireEvent("datachanged", this);
10838 * Query the records by a specified property.
10839 * @param {String} field A field on your records
10840 * @param {String/RegExp} value Either a string that the field
10841 * should start with or a RegExp to test against the field
10842 * @param {Boolean} anyMatch True to match any part not just the beginning
10843 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10845 query : function(property, value, anyMatch){
10846 var fn = this.createFilterFn(property, value, anyMatch);
10847 return fn ? this.queryBy(fn) : this.data.clone();
10851 * Query by a function. The specified function will be called with each
10852 * record in this data source. If the function returns true the record is included
10854 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10855 * @param {Object} scope (optional) The scope of the function (defaults to this)
10856 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10858 queryBy : function(fn, scope){
10859 var data = this.snapshot || this.data;
10860 return data.filterBy(fn, scope||this);
10864 * Collects unique values for a particular dataIndex from this store.
10865 * @param {String} dataIndex The property to collect
10866 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10867 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10868 * @return {Array} An array of the unique values
10870 collect : function(dataIndex, allowNull, bypassFilter){
10871 var d = (bypassFilter === true && this.snapshot) ?
10872 this.snapshot.items : this.data.items;
10873 var v, sv, r = [], l = {};
10874 for(var i = 0, len = d.length; i < len; i++){
10875 v = d[i].data[dataIndex];
10877 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10886 * Revert to a view of the Record cache with no filtering applied.
10887 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10889 clearFilter : function(suppressEvent){
10890 if(this.snapshot && this.snapshot != this.data){
10891 this.data = this.snapshot;
10892 delete this.snapshot;
10893 if(suppressEvent !== true){
10894 this.fireEvent("datachanged", this);
10900 afterEdit : function(record){
10901 if(this.modified.indexOf(record) == -1){
10902 this.modified.push(record);
10904 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10908 afterReject : function(record){
10909 this.modified.remove(record);
10910 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10914 afterCommit : function(record){
10915 this.modified.remove(record);
10916 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10920 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10921 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10923 commitChanges : function(){
10924 var m = this.modified.slice(0);
10925 this.modified = [];
10926 for(var i = 0, len = m.length; i < len; i++){
10932 * Cancel outstanding changes on all changed records.
10934 rejectChanges : function(){
10935 var m = this.modified.slice(0);
10936 this.modified = [];
10937 for(var i = 0, len = m.length; i < len; i++){
10942 onMetaChange : function(meta, rtype, o){
10943 this.recordType = rtype;
10944 this.fields = rtype.prototype.fields;
10945 delete this.snapshot;
10946 this.sortInfo = meta.sortInfo || this.sortInfo;
10947 this.modified = [];
10948 this.fireEvent('metachange', this, this.reader.meta);
10951 moveIndex : function(data, type)
10953 var index = this.indexOf(data);
10955 var newIndex = index + type;
10959 this.insert(newIndex, data);
10964 * Ext JS Library 1.1.1
10965 * Copyright(c) 2006-2007, Ext JS, LLC.
10967 * Originally Released Under LGPL - original licence link has changed is not relivant.
10970 * <script type="text/javascript">
10974 * @class Roo.data.SimpleStore
10975 * @extends Roo.data.Store
10976 * Small helper class to make creating Stores from Array data easier.
10977 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10978 * @cfg {Array} fields An array of field definition objects, or field name strings.
10979 * @cfg {Array} data The multi-dimensional array of data
10981 * @param {Object} config
10983 Roo.data.SimpleStore = function(config){
10984 Roo.data.SimpleStore.superclass.constructor.call(this, {
10986 reader: new Roo.data.ArrayReader({
10989 Roo.data.Record.create(config.fields)
10991 proxy : new Roo.data.MemoryProxy(config.data)
10995 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10997 * Ext JS Library 1.1.1
10998 * Copyright(c) 2006-2007, Ext JS, LLC.
11000 * Originally Released Under LGPL - original licence link has changed is not relivant.
11003 * <script type="text/javascript">
11008 * @extends Roo.data.Store
11009 * @class Roo.data.JsonStore
11010 * Small helper class to make creating Stores for JSON data easier. <br/>
11012 var store = new Roo.data.JsonStore({
11013 url: 'get-images.php',
11015 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11018 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11019 * JsonReader and HttpProxy (unless inline data is provided).</b>
11020 * @cfg {Array} fields An array of field definition objects, or field name strings.
11022 * @param {Object} config
11024 Roo.data.JsonStore = function(c){
11025 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11026 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11027 reader: new Roo.data.JsonReader(c, c.fields)
11030 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11032 * Ext JS Library 1.1.1
11033 * Copyright(c) 2006-2007, Ext JS, LLC.
11035 * Originally Released Under LGPL - original licence link has changed is not relivant.
11038 * <script type="text/javascript">
11042 Roo.data.Field = function(config){
11043 if(typeof config == "string"){
11044 config = {name: config};
11046 Roo.apply(this, config);
11049 this.type = "auto";
11052 var st = Roo.data.SortTypes;
11053 // named sortTypes are supported, here we look them up
11054 if(typeof this.sortType == "string"){
11055 this.sortType = st[this.sortType];
11058 // set default sortType for strings and dates
11059 if(!this.sortType){
11062 this.sortType = st.asUCString;
11065 this.sortType = st.asDate;
11068 this.sortType = st.none;
11073 var stripRe = /[\$,%]/g;
11075 // prebuilt conversion function for this field, instead of
11076 // switching every time we're reading a value
11078 var cv, dateFormat = this.dateFormat;
11083 cv = function(v){ return v; };
11086 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11090 return v !== undefined && v !== null && v !== '' ?
11091 parseInt(String(v).replace(stripRe, ""), 10) : '';
11096 return v !== undefined && v !== null && v !== '' ?
11097 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11102 cv = function(v){ return v === true || v === "true" || v == 1; };
11109 if(v instanceof Date){
11113 if(dateFormat == "timestamp"){
11114 return new Date(v*1000);
11116 return Date.parseDate(v, dateFormat);
11118 var parsed = Date.parse(v);
11119 return parsed ? new Date(parsed) : null;
11128 Roo.data.Field.prototype = {
11136 * Ext JS Library 1.1.1
11137 * Copyright(c) 2006-2007, Ext JS, LLC.
11139 * Originally Released Under LGPL - original licence link has changed is not relivant.
11142 * <script type="text/javascript">
11145 // Base class for reading structured data from a data source. This class is intended to be
11146 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11149 * @class Roo.data.DataReader
11150 * Base class for reading structured data from a data source. This class is intended to be
11151 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11154 Roo.data.DataReader = function(meta, recordType){
11158 this.recordType = recordType instanceof Array ?
11159 Roo.data.Record.create(recordType) : recordType;
11162 Roo.data.DataReader.prototype = {
11164 * Create an empty record
11165 * @param {Object} data (optional) - overlay some values
11166 * @return {Roo.data.Record} record created.
11168 newRow : function(d) {
11170 this.recordType.prototype.fields.each(function(c) {
11172 case 'int' : da[c.name] = 0; break;
11173 case 'date' : da[c.name] = new Date(); break;
11174 case 'float' : da[c.name] = 0.0; break;
11175 case 'boolean' : da[c.name] = false; break;
11176 default : da[c.name] = ""; break;
11180 return new this.recordType(Roo.apply(da, d));
11185 * Ext JS Library 1.1.1
11186 * Copyright(c) 2006-2007, Ext JS, LLC.
11188 * Originally Released Under LGPL - original licence link has changed is not relivant.
11191 * <script type="text/javascript">
11195 * @class Roo.data.DataProxy
11196 * @extends Roo.data.Observable
11197 * This class is an abstract base class for implementations which provide retrieval of
11198 * unformatted data objects.<br>
11200 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11201 * (of the appropriate type which knows how to parse the data object) to provide a block of
11202 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11204 * Custom implementations must implement the load method as described in
11205 * {@link Roo.data.HttpProxy#load}.
11207 Roo.data.DataProxy = function(){
11210 * @event beforeload
11211 * Fires before a network request is made to retrieve a data object.
11212 * @param {Object} This DataProxy object.
11213 * @param {Object} params The params parameter to the load function.
11218 * Fires before the load method's callback is called.
11219 * @param {Object} This DataProxy object.
11220 * @param {Object} o The data object.
11221 * @param {Object} arg The callback argument object passed to the load function.
11225 * @event loadexception
11226 * Fires if an Exception occurs during data retrieval.
11227 * @param {Object} This DataProxy object.
11228 * @param {Object} o The data object.
11229 * @param {Object} arg The callback argument object passed to the load function.
11230 * @param {Object} e The Exception.
11232 loadexception : true
11234 Roo.data.DataProxy.superclass.constructor.call(this);
11237 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11240 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11244 * Ext JS Library 1.1.1
11245 * Copyright(c) 2006-2007, Ext JS, LLC.
11247 * Originally Released Under LGPL - original licence link has changed is not relivant.
11250 * <script type="text/javascript">
11253 * @class Roo.data.MemoryProxy
11254 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11255 * to the Reader when its load method is called.
11257 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11259 Roo.data.MemoryProxy = function(data){
11263 Roo.data.MemoryProxy.superclass.constructor.call(this);
11267 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11270 * Load data from the requested source (in this case an in-memory
11271 * data object passed to the constructor), read the data object into
11272 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11273 * process that block using the passed callback.
11274 * @param {Object} params This parameter is not used by the MemoryProxy class.
11275 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11276 * object into a block of Roo.data.Records.
11277 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11278 * The function must be passed <ul>
11279 * <li>The Record block object</li>
11280 * <li>The "arg" argument from the load function</li>
11281 * <li>A boolean success indicator</li>
11283 * @param {Object} scope The scope in which to call the callback
11284 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11286 load : function(params, reader, callback, scope, arg){
11287 params = params || {};
11290 result = reader.readRecords(this.data);
11292 this.fireEvent("loadexception", this, arg, null, e);
11293 callback.call(scope, null, arg, false);
11296 callback.call(scope, result, arg, true);
11300 update : function(params, records){
11305 * Ext JS Library 1.1.1
11306 * Copyright(c) 2006-2007, Ext JS, LLC.
11308 * Originally Released Under LGPL - original licence link has changed is not relivant.
11311 * <script type="text/javascript">
11314 * @class Roo.data.HttpProxy
11315 * @extends Roo.data.DataProxy
11316 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11317 * configured to reference a certain URL.<br><br>
11319 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11320 * from which the running page was served.<br><br>
11322 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11324 * Be aware that to enable the browser to parse an XML document, the server must set
11325 * the Content-Type header in the HTTP response to "text/xml".
11327 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11328 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11329 * will be used to make the request.
11331 Roo.data.HttpProxy = function(conn){
11332 Roo.data.HttpProxy.superclass.constructor.call(this);
11333 // is conn a conn config or a real conn?
11335 this.useAjax = !conn || !conn.events;
11339 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11340 // thse are take from connection...
11343 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11346 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11347 * extra parameters to each request made by this object. (defaults to undefined)
11350 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11351 * to each request made by this object. (defaults to undefined)
11354 * @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)
11357 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11360 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11366 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11370 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11371 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11372 * a finer-grained basis than the DataProxy events.
11374 getConnection : function(){
11375 return this.useAjax ? Roo.Ajax : this.conn;
11379 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11380 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11381 * process that block using the passed callback.
11382 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11383 * for the request to the remote server.
11384 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11385 * object into a block of Roo.data.Records.
11386 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11387 * The function must be passed <ul>
11388 * <li>The Record block object</li>
11389 * <li>The "arg" argument from the load function</li>
11390 * <li>A boolean success indicator</li>
11392 * @param {Object} scope The scope in which to call the callback
11393 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11395 load : function(params, reader, callback, scope, arg){
11396 if(this.fireEvent("beforeload", this, params) !== false){
11398 params : params || {},
11400 callback : callback,
11405 callback : this.loadResponse,
11409 Roo.applyIf(o, this.conn);
11410 if(this.activeRequest){
11411 Roo.Ajax.abort(this.activeRequest);
11413 this.activeRequest = Roo.Ajax.request(o);
11415 this.conn.request(o);
11418 callback.call(scope||this, null, arg, false);
11423 loadResponse : function(o, success, response){
11424 delete this.activeRequest;
11426 this.fireEvent("loadexception", this, o, response);
11427 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11432 result = o.reader.read(response);
11434 this.fireEvent("loadexception", this, o, response, e);
11435 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11439 this.fireEvent("load", this, o, o.request.arg);
11440 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11444 update : function(dataSet){
11449 updateResponse : function(dataSet){
11454 * Ext JS Library 1.1.1
11455 * Copyright(c) 2006-2007, Ext JS, LLC.
11457 * Originally Released Under LGPL - original licence link has changed is not relivant.
11460 * <script type="text/javascript">
11464 * @class Roo.data.ScriptTagProxy
11465 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11466 * other than the originating domain of the running page.<br><br>
11468 * <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
11469 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11471 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11472 * source code that is used as the source inside a <script> tag.<br><br>
11474 * In order for the browser to process the returned data, the server must wrap the data object
11475 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11476 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11477 * depending on whether the callback name was passed:
11480 boolean scriptTag = false;
11481 String cb = request.getParameter("callback");
11484 response.setContentType("text/javascript");
11486 response.setContentType("application/x-json");
11488 Writer out = response.getWriter();
11490 out.write(cb + "(");
11492 out.print(dataBlock.toJsonString());
11499 * @param {Object} config A configuration object.
11501 Roo.data.ScriptTagProxy = function(config){
11502 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11503 Roo.apply(this, config);
11504 this.head = document.getElementsByTagName("head")[0];
11507 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11509 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11511 * @cfg {String} url The URL from which to request the data object.
11514 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11518 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11519 * the server the name of the callback function set up by the load call to process the returned data object.
11520 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11521 * javascript output which calls this named function passing the data object as its only parameter.
11523 callbackParam : "callback",
11525 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11526 * name to the request.
11531 * Load data from the configured URL, read the data object into
11532 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11533 * process that block using the passed callback.
11534 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11535 * for the request to the remote server.
11536 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11537 * object into a block of Roo.data.Records.
11538 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11539 * The function must be passed <ul>
11540 * <li>The Record block object</li>
11541 * <li>The "arg" argument from the load function</li>
11542 * <li>A boolean success indicator</li>
11544 * @param {Object} scope The scope in which to call the callback
11545 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11547 load : function(params, reader, callback, scope, arg){
11548 if(this.fireEvent("beforeload", this, params) !== false){
11550 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11552 var url = this.url;
11553 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11555 url += "&_dc=" + (new Date().getTime());
11557 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11560 cb : "stcCallback"+transId,
11561 scriptId : "stcScript"+transId,
11565 callback : callback,
11571 window[trans.cb] = function(o){
11572 conn.handleResponse(o, trans);
11575 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11577 if(this.autoAbort !== false){
11581 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11583 var script = document.createElement("script");
11584 script.setAttribute("src", url);
11585 script.setAttribute("type", "text/javascript");
11586 script.setAttribute("id", trans.scriptId);
11587 this.head.appendChild(script);
11589 this.trans = trans;
11591 callback.call(scope||this, null, arg, false);
11596 isLoading : function(){
11597 return this.trans ? true : false;
11601 * Abort the current server request.
11603 abort : function(){
11604 if(this.isLoading()){
11605 this.destroyTrans(this.trans);
11610 destroyTrans : function(trans, isLoaded){
11611 this.head.removeChild(document.getElementById(trans.scriptId));
11612 clearTimeout(trans.timeoutId);
11614 window[trans.cb] = undefined;
11616 delete window[trans.cb];
11619 // if hasn't been loaded, wait for load to remove it to prevent script error
11620 window[trans.cb] = function(){
11621 window[trans.cb] = undefined;
11623 delete window[trans.cb];
11630 handleResponse : function(o, trans){
11631 this.trans = false;
11632 this.destroyTrans(trans, true);
11635 result = trans.reader.readRecords(o);
11637 this.fireEvent("loadexception", this, o, trans.arg, e);
11638 trans.callback.call(trans.scope||window, null, trans.arg, false);
11641 this.fireEvent("load", this, o, trans.arg);
11642 trans.callback.call(trans.scope||window, result, trans.arg, true);
11646 handleFailure : function(trans){
11647 this.trans = false;
11648 this.destroyTrans(trans, false);
11649 this.fireEvent("loadexception", this, null, trans.arg);
11650 trans.callback.call(trans.scope||window, null, trans.arg, false);
11654 * Ext JS Library 1.1.1
11655 * Copyright(c) 2006-2007, Ext JS, LLC.
11657 * Originally Released Under LGPL - original licence link has changed is not relivant.
11660 * <script type="text/javascript">
11664 * @class Roo.data.JsonReader
11665 * @extends Roo.data.DataReader
11666 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11667 * based on mappings in a provided Roo.data.Record constructor.
11669 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11670 * in the reply previously.
11675 var RecordDef = Roo.data.Record.create([
11676 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11677 {name: 'occupation'} // This field will use "occupation" as the mapping.
11679 var myReader = new Roo.data.JsonReader({
11680 totalProperty: "results", // The property which contains the total dataset size (optional)
11681 root: "rows", // The property which contains an Array of row objects
11682 id: "id" // The property within each row object that provides an ID for the record (optional)
11686 * This would consume a JSON file like this:
11688 { 'results': 2, 'rows': [
11689 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11690 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11693 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11694 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11695 * paged from the remote server.
11696 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11697 * @cfg {String} root name of the property which contains the Array of row objects.
11698 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11699 * @cfg {Array} fields Array of field definition objects
11701 * Create a new JsonReader
11702 * @param {Object} meta Metadata configuration options
11703 * @param {Object} recordType Either an Array of field definition objects,
11704 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11706 Roo.data.JsonReader = function(meta, recordType){
11709 // set some defaults:
11710 Roo.applyIf(meta, {
11711 totalProperty: 'total',
11712 successProperty : 'success',
11717 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11719 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11722 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11723 * Used by Store query builder to append _requestMeta to params.
11726 metaFromRemote : false,
11728 * This method is only used by a DataProxy which has retrieved data from a remote server.
11729 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11730 * @return {Object} data A data block which is used by an Roo.data.Store object as
11731 * a cache of Roo.data.Records.
11733 read : function(response){
11734 var json = response.responseText;
11736 var o = /* eval:var:o */ eval("("+json+")");
11738 throw {message: "JsonReader.read: Json object not found"};
11744 this.metaFromRemote = true;
11745 this.meta = o.metaData;
11746 this.recordType = Roo.data.Record.create(o.metaData.fields);
11747 this.onMetaChange(this.meta, this.recordType, o);
11749 return this.readRecords(o);
11752 // private function a store will implement
11753 onMetaChange : function(meta, recordType, o){
11760 simpleAccess: function(obj, subsc) {
11767 getJsonAccessor: function(){
11769 return function(expr) {
11771 return(re.test(expr))
11772 ? new Function("obj", "return obj." + expr)
11777 return Roo.emptyFn;
11782 * Create a data block containing Roo.data.Records from an XML document.
11783 * @param {Object} o An object which contains an Array of row objects in the property specified
11784 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11785 * which contains the total size of the dataset.
11786 * @return {Object} data A data block which is used by an Roo.data.Store object as
11787 * a cache of Roo.data.Records.
11789 readRecords : function(o){
11791 * After any data loads, the raw JSON data is available for further custom processing.
11795 var s = this.meta, Record = this.recordType,
11796 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11798 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11800 if(s.totalProperty) {
11801 this.getTotal = this.getJsonAccessor(s.totalProperty);
11803 if(s.successProperty) {
11804 this.getSuccess = this.getJsonAccessor(s.successProperty);
11806 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11808 var g = this.getJsonAccessor(s.id);
11809 this.getId = function(rec) {
11811 return (r === undefined || r === "") ? null : r;
11814 this.getId = function(){return null;};
11817 for(var jj = 0; jj < fl; jj++){
11819 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11820 this.ef[jj] = this.getJsonAccessor(map);
11824 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11825 if(s.totalProperty){
11826 var vt = parseInt(this.getTotal(o), 10);
11831 if(s.successProperty){
11832 var vs = this.getSuccess(o);
11833 if(vs === false || vs === 'false'){
11838 for(var i = 0; i < c; i++){
11841 var id = this.getId(n);
11842 for(var j = 0; j < fl; j++){
11844 var v = this.ef[j](n);
11846 Roo.log('missing convert for ' + f.name);
11850 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11852 var record = new Record(values, id);
11854 records[i] = record;
11860 totalRecords : totalRecords
11865 * Ext JS Library 1.1.1
11866 * Copyright(c) 2006-2007, Ext JS, LLC.
11868 * Originally Released Under LGPL - original licence link has changed is not relivant.
11871 * <script type="text/javascript">
11875 * @class Roo.data.ArrayReader
11876 * @extends Roo.data.DataReader
11877 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11878 * Each element of that Array represents a row of data fields. The
11879 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11880 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11884 var RecordDef = Roo.data.Record.create([
11885 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11886 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11888 var myReader = new Roo.data.ArrayReader({
11889 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11893 * This would consume an Array like this:
11895 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11897 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11899 * Create a new JsonReader
11900 * @param {Object} meta Metadata configuration options.
11901 * @param {Object} recordType Either an Array of field definition objects
11902 * as specified to {@link Roo.data.Record#create},
11903 * or an {@link Roo.data.Record} object
11904 * created using {@link Roo.data.Record#create}.
11906 Roo.data.ArrayReader = function(meta, recordType){
11907 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11910 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11912 * Create a data block containing Roo.data.Records from an XML document.
11913 * @param {Object} o An Array of row objects which represents the dataset.
11914 * @return {Object} data A data block which is used by an Roo.data.Store object as
11915 * a cache of Roo.data.Records.
11917 readRecords : function(o){
11918 var sid = this.meta ? this.meta.id : null;
11919 var recordType = this.recordType, fields = recordType.prototype.fields;
11922 for(var i = 0; i < root.length; i++){
11925 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11926 for(var j = 0, jlen = fields.length; j < jlen; j++){
11927 var f = fields.items[j];
11928 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11929 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11931 values[f.name] = v;
11933 var record = new recordType(values, id);
11935 records[records.length] = record;
11939 totalRecords : records.length
11948 * @class Roo.bootstrap.ComboBox
11949 * @extends Roo.bootstrap.TriggerField
11950 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11951 * @cfg {Boolean} append (true|false) default false
11952 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11953 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11954 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11955 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11956 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11957 * @cfg {Boolean} animate default true
11958 * @cfg {Boolean} emptyResultText only for touch device
11959 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11961 * Create a new ComboBox.
11962 * @param {Object} config Configuration options
11964 Roo.bootstrap.ComboBox = function(config){
11965 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11969 * Fires when the dropdown list is expanded
11970 * @param {Roo.bootstrap.ComboBox} combo This combo box
11975 * Fires when the dropdown list is collapsed
11976 * @param {Roo.bootstrap.ComboBox} combo This combo box
11980 * @event beforeselect
11981 * Fires before a list item is selected. Return false to cancel the selection.
11982 * @param {Roo.bootstrap.ComboBox} combo This combo box
11983 * @param {Roo.data.Record} record The data record returned from the underlying store
11984 * @param {Number} index The index of the selected item in the dropdown list
11986 'beforeselect' : true,
11989 * Fires when a list item is selected
11990 * @param {Roo.bootstrap.ComboBox} combo This combo box
11991 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11992 * @param {Number} index The index of the selected item in the dropdown list
11996 * @event beforequery
11997 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11998 * The event object passed has these properties:
11999 * @param {Roo.bootstrap.ComboBox} combo This combo box
12000 * @param {String} query The query
12001 * @param {Boolean} forceAll true to force "all" query
12002 * @param {Boolean} cancel true to cancel the query
12003 * @param {Object} e The query event object
12005 'beforequery': true,
12008 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12009 * @param {Roo.bootstrap.ComboBox} combo This combo box
12014 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12015 * @param {Roo.bootstrap.ComboBox} combo This combo box
12016 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12021 * Fires when the remove value from the combobox array
12022 * @param {Roo.bootstrap.ComboBox} combo This combo box
12026 * @event afterremove
12027 * Fires when the remove value from the combobox array
12028 * @param {Roo.bootstrap.ComboBox} combo This combo box
12030 'afterremove' : true,
12032 * @event specialfilter
12033 * Fires when specialfilter
12034 * @param {Roo.bootstrap.ComboBox} combo This combo box
12036 'specialfilter' : true,
12039 * Fires when tick the element
12040 * @param {Roo.bootstrap.ComboBox} combo This combo box
12044 * @event touchviewdisplay
12045 * Fires when touch view require special display (default is using displayField)
12046 * @param {Roo.bootstrap.ComboBox} combo This combo box
12047 * @param {Object} cfg set html .
12049 'touchviewdisplay' : true
12054 this.tickItems = [];
12056 this.selectedIndex = -1;
12057 if(this.mode == 'local'){
12058 if(config.queryDelay === undefined){
12059 this.queryDelay = 10;
12061 if(config.minChars === undefined){
12067 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12070 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12071 * rendering into an Roo.Editor, defaults to false)
12074 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12075 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12078 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12081 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12082 * the dropdown list (defaults to undefined, with no header element)
12086 * @cfg {String/Roo.Template} tpl The template to use to render the output
12090 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12092 listWidth: undefined,
12094 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12095 * mode = 'remote' or 'text' if mode = 'local')
12097 displayField: undefined,
12100 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12101 * mode = 'remote' or 'value' if mode = 'local').
12102 * Note: use of a valueField requires the user make a selection
12103 * in order for a value to be mapped.
12105 valueField: undefined,
12107 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12112 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12113 * field's data value (defaults to the underlying DOM element's name)
12115 hiddenName: undefined,
12117 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12121 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12123 selectedClass: 'active',
12126 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12130 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12131 * anchor positions (defaults to 'tl-bl')
12133 listAlign: 'tl-bl?',
12135 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12139 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12140 * query specified by the allQuery config option (defaults to 'query')
12142 triggerAction: 'query',
12144 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12145 * (defaults to 4, does not apply if editable = false)
12149 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12150 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12154 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12155 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12159 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12160 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12164 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12165 * when editable = true (defaults to false)
12167 selectOnFocus:false,
12169 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12171 queryParam: 'query',
12173 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12174 * when mode = 'remote' (defaults to 'Loading...')
12176 loadingText: 'Loading...',
12178 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12182 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12186 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12187 * traditional select (defaults to true)
12191 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12195 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12199 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12200 * listWidth has a higher value)
12204 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12205 * allow the user to set arbitrary text into the field (defaults to false)
12207 forceSelection:false,
12209 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12210 * if typeAhead = true (defaults to 250)
12212 typeAheadDelay : 250,
12214 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12215 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12217 valueNotFoundText : undefined,
12219 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12221 blockFocus : false,
12224 * @cfg {Boolean} disableClear Disable showing of clear button.
12226 disableClear : false,
12228 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12230 alwaysQuery : false,
12233 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12238 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12240 invalidClass : "has-warning",
12243 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12245 validClass : "has-success",
12248 * @cfg {Boolean} specialFilter (true|false) special filter default false
12250 specialFilter : false,
12253 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12255 mobileTouchView : true,
12258 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12260 useNativeIOS : false,
12262 ios_options : false,
12274 btnPosition : 'right',
12275 triggerList : true,
12276 showToggleBtn : true,
12278 emptyResultText: 'Empty',
12279 triggerText : 'Select',
12281 // element that contains real text value.. (when hidden is used..)
12283 getAutoCreate : function()
12288 * Render classic select for iso
12291 if(Roo.isIOS && this.useNativeIOS){
12292 cfg = this.getAutoCreateNativeIOS();
12300 if(Roo.isTouch && this.mobileTouchView){
12301 cfg = this.getAutoCreateTouchView();
12308 if(!this.tickable){
12309 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12314 * ComboBox with tickable selections
12317 var align = this.labelAlign || this.parentLabelAlign();
12320 cls : 'form-group roo-combobox-tickable' //input-group
12325 cls : 'tickable-buttons',
12330 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12331 html : this.triggerText
12337 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12344 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12351 buttons.cn.unshift({
12353 cls: 'roo-select2-search-field-input'
12359 Roo.each(buttons.cn, function(c){
12361 c.cls += ' btn-' + _this.size;
12364 if (_this.disabled) {
12375 cls: 'form-hidden-field'
12379 cls: 'roo-select2-choices',
12383 cls: 'roo-select2-search-field',
12395 cls: 'roo-select2-container input-group roo-select2-container-multi',
12400 // cls: 'typeahead typeahead-long dropdown-menu',
12401 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12406 if(this.hasFeedback && !this.allowBlank){
12410 cls: 'glyphicon form-control-feedback'
12413 combobox.cn.push(feedback);
12416 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12418 // Roo.log("left and has label");
12422 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12423 tooltip : 'This field is required'
12428 cls : 'control-label col-sm-' + this.labelWidth,
12429 html : this.fieldLabel
12433 cls : "col-sm-" + (12 - this.labelWidth),
12441 if(this.indicatorpos == 'right'){
12447 cls : 'control-label col-sm-' + this.labelWidth,
12448 html : this.fieldLabel
12453 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12454 tooltip : 'This field is required'
12457 cls : "col-sm-" + (12 - this.labelWidth),
12468 } else if ( this.fieldLabel.length) {
12469 // Roo.log(" label");
12473 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12474 tooltip : 'This field is required'
12478 //cls : 'input-group-addon',
12479 html : this.fieldLabel
12487 if(this.indicatorpos == 'right'){
12492 //cls : 'input-group-addon',
12493 html : this.fieldLabel
12499 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12500 tooltip : 'This field is required'
12511 // Roo.log(" no label && no align");
12518 ['xs','sm','md','lg'].map(function(size){
12519 if (settings[size]) {
12520 cfg.cls += ' col-' + size + '-' + settings[size];
12528 _initEventsCalled : false,
12531 initEvents: function()
12533 if (this._initEventsCalled) { // as we call render... prevent looping...
12536 this._initEventsCalled = true;
12539 throw "can not find store for combo";
12542 this.store = Roo.factory(this.store, Roo.data);
12544 // if we are building from html. then this element is so complex, that we can not really
12545 // use the rendered HTML.
12546 // so we have to trash and replace the previous code.
12547 if (Roo.XComponent.build_from_html) {
12549 // remove this element....
12550 var e = this.el.dom, k=0;
12551 while (e ) { e = e.previousSibling; ++k;}
12556 this.rendered = false;
12558 this.render(this.parent().getChildContainer(true), k);
12564 if(Roo.isIOS && this.useNativeIOS){
12565 this.initIOSView();
12573 if(Roo.isTouch && this.mobileTouchView){
12574 this.initTouchView();
12579 this.initTickableEvents();
12583 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12585 if(this.hiddenName){
12587 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12589 this.hiddenField.dom.value =
12590 this.hiddenValue !== undefined ? this.hiddenValue :
12591 this.value !== undefined ? this.value : '';
12593 // prevent input submission
12594 this.el.dom.removeAttribute('name');
12595 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12600 // this.el.dom.setAttribute('autocomplete', 'off');
12603 var cls = 'x-combo-list';
12605 //this.list = new Roo.Layer({
12606 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12612 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12613 _this.list.setWidth(lw);
12616 this.list.on('mouseover', this.onViewOver, this);
12617 this.list.on('mousemove', this.onViewMove, this);
12619 this.list.on('scroll', this.onViewScroll, this);
12622 this.list.swallowEvent('mousewheel');
12623 this.assetHeight = 0;
12626 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12627 this.assetHeight += this.header.getHeight();
12630 this.innerList = this.list.createChild({cls:cls+'-inner'});
12631 this.innerList.on('mouseover', this.onViewOver, this);
12632 this.innerList.on('mousemove', this.onViewMove, this);
12633 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12635 if(this.allowBlank && !this.pageSize && !this.disableClear){
12636 this.footer = this.list.createChild({cls:cls+'-ft'});
12637 this.pageTb = new Roo.Toolbar(this.footer);
12641 this.footer = this.list.createChild({cls:cls+'-ft'});
12642 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12643 {pageSize: this.pageSize});
12647 if (this.pageTb && this.allowBlank && !this.disableClear) {
12649 this.pageTb.add(new Roo.Toolbar.Fill(), {
12650 cls: 'x-btn-icon x-btn-clear',
12652 handler: function()
12655 _this.clearValue();
12656 _this.onSelect(false, -1);
12661 this.assetHeight += this.footer.getHeight();
12666 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12669 this.view = new Roo.View(this.list, this.tpl, {
12670 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12672 //this.view.wrapEl.setDisplayed(false);
12673 this.view.on('click', this.onViewClick, this);
12677 this.store.on('beforeload', this.onBeforeLoad, this);
12678 this.store.on('load', this.onLoad, this);
12679 this.store.on('loadexception', this.onLoadException, this);
12681 if(this.resizable){
12682 this.resizer = new Roo.Resizable(this.list, {
12683 pinned:true, handles:'se'
12685 this.resizer.on('resize', function(r, w, h){
12686 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12687 this.listWidth = w;
12688 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12689 this.restrictHeight();
12691 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12694 if(!this.editable){
12695 this.editable = true;
12696 this.setEditable(false);
12701 if (typeof(this.events.add.listeners) != 'undefined') {
12703 this.addicon = this.wrap.createChild(
12704 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12706 this.addicon.on('click', function(e) {
12707 this.fireEvent('add', this);
12710 if (typeof(this.events.edit.listeners) != 'undefined') {
12712 this.editicon = this.wrap.createChild(
12713 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12714 if (this.addicon) {
12715 this.editicon.setStyle('margin-left', '40px');
12717 this.editicon.on('click', function(e) {
12719 // we fire even if inothing is selected..
12720 this.fireEvent('edit', this, this.lastData );
12726 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12727 "up" : function(e){
12728 this.inKeyMode = true;
12732 "down" : function(e){
12733 if(!this.isExpanded()){
12734 this.onTriggerClick();
12736 this.inKeyMode = true;
12741 "enter" : function(e){
12742 // this.onViewClick();
12746 if(this.fireEvent("specialkey", this, e)){
12747 this.onViewClick(false);
12753 "esc" : function(e){
12757 "tab" : function(e){
12760 if(this.fireEvent("specialkey", this, e)){
12761 this.onViewClick(false);
12769 doRelay : function(foo, bar, hname){
12770 if(hname == 'down' || this.scope.isExpanded()){
12771 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12780 this.queryDelay = Math.max(this.queryDelay || 10,
12781 this.mode == 'local' ? 10 : 250);
12784 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12786 if(this.typeAhead){
12787 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12789 if(this.editable !== false){
12790 this.inputEl().on("keyup", this.onKeyUp, this);
12792 if(this.forceSelection){
12793 this.inputEl().on('blur', this.doForce, this);
12797 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12798 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12802 initTickableEvents: function()
12806 if(this.hiddenName){
12808 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12810 this.hiddenField.dom.value =
12811 this.hiddenValue !== undefined ? this.hiddenValue :
12812 this.value !== undefined ? this.value : '';
12814 // prevent input submission
12815 this.el.dom.removeAttribute('name');
12816 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12821 // this.list = this.el.select('ul.dropdown-menu',true).first();
12823 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12824 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12825 if(this.triggerList){
12826 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12829 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12830 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12832 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12833 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12835 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12836 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12838 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12839 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12840 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12843 this.cancelBtn.hide();
12848 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12849 _this.list.setWidth(lw);
12852 this.list.on('mouseover', this.onViewOver, this);
12853 this.list.on('mousemove', this.onViewMove, this);
12855 this.list.on('scroll', this.onViewScroll, this);
12858 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>';
12861 this.view = new Roo.View(this.list, this.tpl, {
12862 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12865 //this.view.wrapEl.setDisplayed(false);
12866 this.view.on('click', this.onViewClick, this);
12870 this.store.on('beforeload', this.onBeforeLoad, this);
12871 this.store.on('load', this.onLoad, this);
12872 this.store.on('loadexception', this.onLoadException, this);
12875 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12876 "up" : function(e){
12877 this.inKeyMode = true;
12881 "down" : function(e){
12882 this.inKeyMode = true;
12886 "enter" : function(e){
12887 if(this.fireEvent("specialkey", this, e)){
12888 this.onViewClick(false);
12894 "esc" : function(e){
12895 this.onTickableFooterButtonClick(e, false, false);
12898 "tab" : function(e){
12899 this.fireEvent("specialkey", this, e);
12901 this.onTickableFooterButtonClick(e, false, false);
12908 doRelay : function(e, fn, key){
12909 if(this.scope.isExpanded()){
12910 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12919 this.queryDelay = Math.max(this.queryDelay || 10,
12920 this.mode == 'local' ? 10 : 250);
12923 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12925 if(this.typeAhead){
12926 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12929 if(this.editable !== false){
12930 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12935 onDestroy : function(){
12937 this.view.setStore(null);
12938 this.view.el.removeAllListeners();
12939 this.view.el.remove();
12940 this.view.purgeListeners();
12943 this.list.dom.innerHTML = '';
12947 this.store.un('beforeload', this.onBeforeLoad, this);
12948 this.store.un('load', this.onLoad, this);
12949 this.store.un('loadexception', this.onLoadException, this);
12951 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12955 fireKey : function(e){
12956 if(e.isNavKeyPress() && !this.list.isVisible()){
12957 this.fireEvent("specialkey", this, e);
12962 onResize: function(w, h){
12963 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12965 // if(typeof w != 'number'){
12966 // // we do not handle it!?!?
12969 // var tw = this.trigger.getWidth();
12970 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12971 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12973 // this.inputEl().setWidth( this.adjustWidth('input', x));
12975 // //this.trigger.setStyle('left', x+'px');
12977 // if(this.list && this.listWidth === undefined){
12978 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12979 // this.list.setWidth(lw);
12980 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12988 * Allow or prevent the user from directly editing the field text. If false is passed,
12989 * the user will only be able to select from the items defined in the dropdown list. This method
12990 * is the runtime equivalent of setting the 'editable' config option at config time.
12991 * @param {Boolean} value True to allow the user to directly edit the field text
12993 setEditable : function(value){
12994 if(value == this.editable){
12997 this.editable = value;
12999 this.inputEl().dom.setAttribute('readOnly', true);
13000 this.inputEl().on('mousedown', this.onTriggerClick, this);
13001 this.inputEl().addClass('x-combo-noedit');
13003 this.inputEl().dom.setAttribute('readOnly', false);
13004 this.inputEl().un('mousedown', this.onTriggerClick, this);
13005 this.inputEl().removeClass('x-combo-noedit');
13011 onBeforeLoad : function(combo,opts){
13012 if(!this.hasFocus){
13016 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13018 this.restrictHeight();
13019 this.selectedIndex = -1;
13023 onLoad : function(){
13025 this.hasQuery = false;
13027 if(!this.hasFocus){
13031 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13032 this.loading.hide();
13035 if(this.store.getCount() > 0){
13037 this.restrictHeight();
13038 if(this.lastQuery == this.allQuery){
13039 if(this.editable && !this.tickable){
13040 this.inputEl().dom.select();
13044 !this.selectByValue(this.value, true) &&
13047 !this.store.lastOptions ||
13048 typeof(this.store.lastOptions.add) == 'undefined' ||
13049 this.store.lastOptions.add != true
13052 this.select(0, true);
13055 if(this.autoFocus){
13058 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13059 this.taTask.delay(this.typeAheadDelay);
13063 this.onEmptyResults();
13069 onLoadException : function()
13071 this.hasQuery = false;
13073 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13074 this.loading.hide();
13077 if(this.tickable && this.editable){
13082 // only causes errors at present
13083 //Roo.log(this.store.reader.jsonData);
13084 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13086 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13092 onTypeAhead : function(){
13093 if(this.store.getCount() > 0){
13094 var r = this.store.getAt(0);
13095 var newValue = r.data[this.displayField];
13096 var len = newValue.length;
13097 var selStart = this.getRawValue().length;
13099 if(selStart != len){
13100 this.setRawValue(newValue);
13101 this.selectText(selStart, newValue.length);
13107 onSelect : function(record, index){
13109 if(this.fireEvent('beforeselect', this, record, index) !== false){
13111 this.setFromData(index > -1 ? record.data : false);
13114 this.fireEvent('select', this, record, index);
13119 * Returns the currently selected field value or empty string if no value is set.
13120 * @return {String} value The selected value
13122 getValue : function()
13124 if(Roo.isIOS && this.useNativeIOS){
13125 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13129 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13132 if(this.valueField){
13133 return typeof this.value != 'undefined' ? this.value : '';
13135 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13139 getRawValue : function()
13141 if(Roo.isIOS && this.useNativeIOS){
13142 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13145 var v = this.inputEl().getValue();
13151 * Clears any text/value currently set in the field
13153 clearValue : function(){
13155 if(this.hiddenField){
13156 this.hiddenField.dom.value = '';
13159 this.setRawValue('');
13160 this.lastSelectionText = '';
13161 this.lastData = false;
13163 var close = this.closeTriggerEl();
13174 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13175 * will be displayed in the field. If the value does not match the data value of an existing item,
13176 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13177 * Otherwise the field will be blank (although the value will still be set).
13178 * @param {String} value The value to match
13180 setValue : function(v)
13182 if(Roo.isIOS && this.useNativeIOS){
13183 this.setIOSValue(v);
13193 if(this.valueField){
13194 var r = this.findRecord(this.valueField, v);
13196 text = r.data[this.displayField];
13197 }else if(this.valueNotFoundText !== undefined){
13198 text = this.valueNotFoundText;
13201 this.lastSelectionText = text;
13202 if(this.hiddenField){
13203 this.hiddenField.dom.value = v;
13205 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13208 var close = this.closeTriggerEl();
13211 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13217 * @property {Object} the last set data for the element
13222 * Sets the value of the field based on a object which is related to the record format for the store.
13223 * @param {Object} value the value to set as. or false on reset?
13225 setFromData : function(o){
13232 var dv = ''; // display value
13233 var vv = ''; // value value..
13235 if (this.displayField) {
13236 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13238 // this is an error condition!!!
13239 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13242 if(this.valueField){
13243 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13246 var close = this.closeTriggerEl();
13249 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13252 if(this.hiddenField){
13253 this.hiddenField.dom.value = vv;
13255 this.lastSelectionText = dv;
13256 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13260 // no hidden field.. - we store the value in 'value', but still display
13261 // display field!!!!
13262 this.lastSelectionText = dv;
13263 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13270 reset : function(){
13271 // overridden so that last data is reset..
13278 this.setValue(this.originalValue);
13279 //this.clearInvalid();
13280 this.lastData = false;
13282 this.view.clearSelections();
13288 findRecord : function(prop, value){
13290 if(this.store.getCount() > 0){
13291 this.store.each(function(r){
13292 if(r.data[prop] == value){
13302 getName: function()
13304 // returns hidden if it's set..
13305 if (!this.rendered) {return ''};
13306 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13310 onViewMove : function(e, t){
13311 this.inKeyMode = false;
13315 onViewOver : function(e, t){
13316 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13319 var item = this.view.findItemFromChild(t);
13322 var index = this.view.indexOf(item);
13323 this.select(index, false);
13328 onViewClick : function(view, doFocus, el, e)
13330 var index = this.view.getSelectedIndexes()[0];
13332 var r = this.store.getAt(index);
13336 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13343 Roo.each(this.tickItems, function(v,k){
13345 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13347 _this.tickItems.splice(k, 1);
13349 if(typeof(e) == 'undefined' && view == false){
13350 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13362 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13363 this.tickItems.push(r.data);
13366 if(typeof(e) == 'undefined' && view == false){
13367 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13374 this.onSelect(r, index);
13376 if(doFocus !== false && !this.blockFocus){
13377 this.inputEl().focus();
13382 restrictHeight : function(){
13383 //this.innerList.dom.style.height = '';
13384 //var inner = this.innerList.dom;
13385 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13386 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13387 //this.list.beginUpdate();
13388 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13389 this.list.alignTo(this.inputEl(), this.listAlign);
13390 this.list.alignTo(this.inputEl(), this.listAlign);
13391 //this.list.endUpdate();
13395 onEmptyResults : function(){
13397 if(this.tickable && this.editable){
13398 this.restrictHeight();
13406 * Returns true if the dropdown list is expanded, else false.
13408 isExpanded : function(){
13409 return this.list.isVisible();
13413 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13414 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13415 * @param {String} value The data value of the item to select
13416 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13417 * selected item if it is not currently in view (defaults to true)
13418 * @return {Boolean} True if the value matched an item in the list, else false
13420 selectByValue : function(v, scrollIntoView){
13421 if(v !== undefined && v !== null){
13422 var r = this.findRecord(this.valueField || this.displayField, v);
13424 this.select(this.store.indexOf(r), scrollIntoView);
13432 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13433 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13434 * @param {Number} index The zero-based index of the list item to select
13435 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13436 * selected item if it is not currently in view (defaults to true)
13438 select : function(index, scrollIntoView){
13439 this.selectedIndex = index;
13440 this.view.select(index);
13441 if(scrollIntoView !== false){
13442 var el = this.view.getNode(index);
13444 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13447 this.list.scrollChildIntoView(el, false);
13453 selectNext : function(){
13454 var ct = this.store.getCount();
13456 if(this.selectedIndex == -1){
13458 }else if(this.selectedIndex < ct-1){
13459 this.select(this.selectedIndex+1);
13465 selectPrev : function(){
13466 var ct = this.store.getCount();
13468 if(this.selectedIndex == -1){
13470 }else if(this.selectedIndex != 0){
13471 this.select(this.selectedIndex-1);
13477 onKeyUp : function(e){
13478 if(this.editable !== false && !e.isSpecialKey()){
13479 this.lastKey = e.getKey();
13480 this.dqTask.delay(this.queryDelay);
13485 validateBlur : function(){
13486 return !this.list || !this.list.isVisible();
13490 initQuery : function(){
13492 var v = this.getRawValue();
13494 if(this.tickable && this.editable){
13495 v = this.tickableInputEl().getValue();
13502 doForce : function(){
13503 if(this.inputEl().dom.value.length > 0){
13504 this.inputEl().dom.value =
13505 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13511 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13512 * query allowing the query action to be canceled if needed.
13513 * @param {String} query The SQL query to execute
13514 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13515 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13516 * saved in the current store (defaults to false)
13518 doQuery : function(q, forceAll){
13520 if(q === undefined || q === null){
13525 forceAll: forceAll,
13529 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13534 forceAll = qe.forceAll;
13535 if(forceAll === true || (q.length >= this.minChars)){
13537 this.hasQuery = true;
13539 if(this.lastQuery != q || this.alwaysQuery){
13540 this.lastQuery = q;
13541 if(this.mode == 'local'){
13542 this.selectedIndex = -1;
13544 this.store.clearFilter();
13547 if(this.specialFilter){
13548 this.fireEvent('specialfilter', this);
13553 this.store.filter(this.displayField, q);
13556 this.store.fireEvent("datachanged", this.store);
13563 this.store.baseParams[this.queryParam] = q;
13565 var options = {params : this.getParams(q)};
13568 options.add = true;
13569 options.params.start = this.page * this.pageSize;
13572 this.store.load(options);
13575 * this code will make the page width larger, at the beginning, the list not align correctly,
13576 * we should expand the list on onLoad
13577 * so command out it
13582 this.selectedIndex = -1;
13587 this.loadNext = false;
13591 getParams : function(q){
13593 //p[this.queryParam] = q;
13597 p.limit = this.pageSize;
13603 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13605 collapse : function(){
13606 if(!this.isExpanded()){
13613 this.hasFocus = false;
13615 this.cancelBtn.hide();
13616 this.trigger.show();
13619 this.tickableInputEl().dom.value = '';
13620 this.tickableInputEl().blur();
13625 Roo.get(document).un('mousedown', this.collapseIf, this);
13626 Roo.get(document).un('mousewheel', this.collapseIf, this);
13627 if (!this.editable) {
13628 Roo.get(document).un('keydown', this.listKeyPress, this);
13630 this.fireEvent('collapse', this);
13636 collapseIf : function(e){
13637 var in_combo = e.within(this.el);
13638 var in_list = e.within(this.list);
13639 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13641 if (in_combo || in_list || is_list) {
13642 //e.stopPropagation();
13647 this.onTickableFooterButtonClick(e, false, false);
13655 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13657 expand : function(){
13659 if(this.isExpanded() || !this.hasFocus){
13663 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13664 this.list.setWidth(lw);
13671 this.restrictHeight();
13675 this.tickItems = Roo.apply([], this.item);
13678 this.cancelBtn.show();
13679 this.trigger.hide();
13682 this.tickableInputEl().focus();
13687 Roo.get(document).on('mousedown', this.collapseIf, this);
13688 Roo.get(document).on('mousewheel', this.collapseIf, this);
13689 if (!this.editable) {
13690 Roo.get(document).on('keydown', this.listKeyPress, this);
13693 this.fireEvent('expand', this);
13697 // Implements the default empty TriggerField.onTriggerClick function
13698 onTriggerClick : function(e)
13700 Roo.log('trigger click');
13702 if(this.disabled || !this.triggerList){
13707 this.loadNext = false;
13709 if(this.isExpanded()){
13711 if (!this.blockFocus) {
13712 this.inputEl().focus();
13716 this.hasFocus = true;
13717 if(this.triggerAction == 'all') {
13718 this.doQuery(this.allQuery, true);
13720 this.doQuery(this.getRawValue());
13722 if (!this.blockFocus) {
13723 this.inputEl().focus();
13728 onTickableTriggerClick : function(e)
13735 this.loadNext = false;
13736 this.hasFocus = true;
13738 if(this.triggerAction == 'all') {
13739 this.doQuery(this.allQuery, true);
13741 this.doQuery(this.getRawValue());
13745 onSearchFieldClick : function(e)
13747 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13748 this.onTickableFooterButtonClick(e, false, false);
13752 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13757 this.loadNext = false;
13758 this.hasFocus = true;
13760 if(this.triggerAction == 'all') {
13761 this.doQuery(this.allQuery, true);
13763 this.doQuery(this.getRawValue());
13767 listKeyPress : function(e)
13769 //Roo.log('listkeypress');
13770 // scroll to first matching element based on key pres..
13771 if (e.isSpecialKey()) {
13774 var k = String.fromCharCode(e.getKey()).toUpperCase();
13777 var csel = this.view.getSelectedNodes();
13778 var cselitem = false;
13780 var ix = this.view.indexOf(csel[0]);
13781 cselitem = this.store.getAt(ix);
13782 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13788 this.store.each(function(v) {
13790 // start at existing selection.
13791 if (cselitem.id == v.id) {
13797 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13798 match = this.store.indexOf(v);
13804 if (match === false) {
13805 return true; // no more action?
13808 this.view.select(match);
13809 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13810 sn.scrollIntoView(sn.dom.parentNode, false);
13813 onViewScroll : function(e, t){
13815 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){
13819 this.hasQuery = true;
13821 this.loading = this.list.select('.loading', true).first();
13823 if(this.loading === null){
13824 this.list.createChild({
13826 cls: 'loading roo-select2-more-results roo-select2-active',
13827 html: 'Loading more results...'
13830 this.loading = this.list.select('.loading', true).first();
13832 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13834 this.loading.hide();
13837 this.loading.show();
13842 this.loadNext = true;
13844 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13849 addItem : function(o)
13851 var dv = ''; // display value
13853 if (this.displayField) {
13854 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13856 // this is an error condition!!!
13857 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13864 var choice = this.choices.createChild({
13866 cls: 'roo-select2-search-choice',
13875 cls: 'roo-select2-search-choice-close',
13880 }, this.searchField);
13882 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13884 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13892 this.inputEl().dom.value = '';
13897 onRemoveItem : function(e, _self, o)
13899 e.preventDefault();
13901 this.lastItem = Roo.apply([], this.item);
13903 var index = this.item.indexOf(o.data) * 1;
13906 Roo.log('not this item?!');
13910 this.item.splice(index, 1);
13915 this.fireEvent('remove', this, e);
13921 syncValue : function()
13923 if(!this.item.length){
13930 Roo.each(this.item, function(i){
13931 if(_this.valueField){
13932 value.push(i[_this.valueField]);
13939 this.value = value.join(',');
13941 if(this.hiddenField){
13942 this.hiddenField.dom.value = this.value;
13945 this.store.fireEvent("datachanged", this.store);
13950 clearItem : function()
13952 if(!this.multiple){
13958 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13966 if(this.tickable && !Roo.isTouch){
13967 this.view.refresh();
13971 inputEl: function ()
13973 if(Roo.isIOS && this.useNativeIOS){
13974 return this.el.select('select.roo-ios-select', true).first();
13977 if(Roo.isTouch && this.mobileTouchView){
13978 return this.el.select('input.form-control',true).first();
13982 return this.searchField;
13985 return this.el.select('input.form-control',true).first();
13988 onTickableFooterButtonClick : function(e, btn, el)
13990 e.preventDefault();
13992 this.lastItem = Roo.apply([], this.item);
13994 if(btn && btn.name == 'cancel'){
13995 this.tickItems = Roo.apply([], this.item);
14004 Roo.each(this.tickItems, function(o){
14012 validate : function()
14014 var v = this.getRawValue();
14017 v = this.getValue();
14020 if(this.disabled || this.allowBlank || v.length){
14025 this.markInvalid();
14029 tickableInputEl : function()
14031 if(!this.tickable || !this.editable){
14032 return this.inputEl();
14035 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14039 getAutoCreateTouchView : function()
14044 cls: 'form-group' //input-group
14050 type : this.inputType,
14051 cls : 'form-control x-combo-noedit',
14052 autocomplete: 'new-password',
14053 placeholder : this.placeholder || '',
14058 input.name = this.name;
14062 input.cls += ' input-' + this.size;
14065 if (this.disabled) {
14066 input.disabled = true;
14077 inputblock.cls += ' input-group';
14079 inputblock.cn.unshift({
14081 cls : 'input-group-addon',
14086 if(this.removable && !this.multiple){
14087 inputblock.cls += ' roo-removable';
14089 inputblock.cn.push({
14092 cls : 'roo-combo-removable-btn close'
14096 if(this.hasFeedback && !this.allowBlank){
14098 inputblock.cls += ' has-feedback';
14100 inputblock.cn.push({
14102 cls: 'glyphicon form-control-feedback'
14109 inputblock.cls += (this.before) ? '' : ' input-group';
14111 inputblock.cn.push({
14113 cls : 'input-group-addon',
14124 cls: 'form-hidden-field'
14138 cls: 'form-hidden-field'
14142 cls: 'roo-select2-choices',
14146 cls: 'roo-select2-search-field',
14159 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14165 if(!this.multiple && this.showToggleBtn){
14172 if (this.caret != false) {
14175 cls: 'fa fa-' + this.caret
14182 cls : 'input-group-addon btn dropdown-toggle',
14187 cls: 'combobox-clear',
14201 combobox.cls += ' roo-select2-container-multi';
14204 var align = this.labelAlign || this.parentLabelAlign();
14208 if(this.fieldLabel.length && this.labelWidth){
14210 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14211 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14216 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14217 tooltip : 'This field is required'
14221 cls : 'control-label ' + lw,
14222 html : this.fieldLabel
14233 if(this.indicatorpos == 'right'){
14237 cls : 'control-label ' + lw,
14238 html : this.fieldLabel
14243 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14244 tooltip : 'This field is required'
14256 var settings = this;
14258 ['xs','sm','md','lg'].map(function(size){
14259 if (settings[size]) {
14260 cfg.cls += ' col-' + size + '-' + settings[size];
14267 initTouchView : function()
14269 this.renderTouchView();
14271 this.touchViewEl.on('scroll', function(){
14272 this.el.dom.scrollTop = 0;
14275 this.originalValue = this.getValue();
14277 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14279 this.inputEl().on("click", this.showTouchView, this);
14280 if (this.triggerEl) {
14281 this.triggerEl.on("click", this.showTouchView, this);
14285 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14286 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14288 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14290 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14291 this.store.on('load', this.onTouchViewLoad, this);
14292 this.store.on('loadexception', this.onTouchViewLoadException, this);
14294 if(this.hiddenName){
14296 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14298 this.hiddenField.dom.value =
14299 this.hiddenValue !== undefined ? this.hiddenValue :
14300 this.value !== undefined ? this.value : '';
14302 this.el.dom.removeAttribute('name');
14303 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14307 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14308 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14311 if(this.removable && !this.multiple){
14312 var close = this.closeTriggerEl();
14314 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14315 close.on('click', this.removeBtnClick, this, close);
14319 * fix the bug in Safari iOS8
14321 this.inputEl().on("focus", function(e){
14322 document.activeElement.blur();
14330 renderTouchView : function()
14332 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14333 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14335 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14336 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14338 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14339 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14340 this.touchViewBodyEl.setStyle('overflow', 'auto');
14342 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14343 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14345 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14346 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14350 showTouchView : function()
14356 this.touchViewHeaderEl.hide();
14358 if(this.modalTitle.length){
14359 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14360 this.touchViewHeaderEl.show();
14363 this.touchViewEl.show();
14365 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14366 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14367 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14369 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14371 if(this.modalTitle.length){
14372 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14375 this.touchViewBodyEl.setHeight(bodyHeight);
14379 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14381 this.touchViewEl.addClass('in');
14384 this.doTouchViewQuery();
14388 hideTouchView : function()
14390 this.touchViewEl.removeClass('in');
14394 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14396 this.touchViewEl.setStyle('display', 'none');
14401 setTouchViewValue : function()
14408 Roo.each(this.tickItems, function(o){
14413 this.hideTouchView();
14416 doTouchViewQuery : function()
14425 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14429 if(!this.alwaysQuery || this.mode == 'local'){
14430 this.onTouchViewLoad();
14437 onTouchViewBeforeLoad : function(combo,opts)
14443 onTouchViewLoad : function()
14445 if(this.store.getCount() < 1){
14446 this.onTouchViewEmptyResults();
14450 this.clearTouchView();
14452 var rawValue = this.getRawValue();
14454 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14456 this.tickItems = [];
14458 this.store.data.each(function(d, rowIndex){
14459 var row = this.touchViewListGroup.createChild(template);
14461 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14462 row.addClass(d.data.cls);
14465 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14468 html : d.data[this.displayField]
14471 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14472 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14475 row.removeClass('selected');
14476 if(!this.multiple && this.valueField &&
14477 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14480 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14481 row.addClass('selected');
14484 if(this.multiple && this.valueField &&
14485 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14489 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14490 this.tickItems.push(d.data);
14493 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14497 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14499 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14501 if(this.modalTitle.length){
14502 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14505 var listHeight = this.touchViewListGroup.getHeight();
14509 if(firstChecked && listHeight > bodyHeight){
14510 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14515 onTouchViewLoadException : function()
14517 this.hideTouchView();
14520 onTouchViewEmptyResults : function()
14522 this.clearTouchView();
14524 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14526 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14530 clearTouchView : function()
14532 this.touchViewListGroup.dom.innerHTML = '';
14535 onTouchViewClick : function(e, el, o)
14537 e.preventDefault();
14540 var rowIndex = o.rowIndex;
14542 var r = this.store.getAt(rowIndex);
14544 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14546 if(!this.multiple){
14547 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14548 c.dom.removeAttribute('checked');
14551 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14553 this.setFromData(r.data);
14555 var close = this.closeTriggerEl();
14561 this.hideTouchView();
14563 this.fireEvent('select', this, r, rowIndex);
14568 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14569 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14570 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14574 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14575 this.addItem(r.data);
14576 this.tickItems.push(r.data);
14580 getAutoCreateNativeIOS : function()
14583 cls: 'form-group' //input-group,
14588 cls : 'roo-ios-select'
14592 combobox.name = this.name;
14595 if (this.disabled) {
14596 combobox.disabled = true;
14599 var settings = this;
14601 ['xs','sm','md','lg'].map(function(size){
14602 if (settings[size]) {
14603 cfg.cls += ' col-' + size + '-' + settings[size];
14613 initIOSView : function()
14615 this.store.on('load', this.onIOSViewLoad, this);
14620 onIOSViewLoad : function()
14622 if(this.store.getCount() < 1){
14626 this.clearIOSView();
14628 if(this.allowBlank) {
14630 var default_text = '-- SELECT --';
14632 var opt = this.inputEl().createChild({
14635 html : default_text
14639 o[this.valueField] = 0;
14640 o[this.displayField] = default_text;
14642 this.ios_options.push({
14649 this.store.data.each(function(d, rowIndex){
14653 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14654 html = d.data[this.displayField];
14659 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14660 value = d.data[this.valueField];
14669 if(this.value == d.data[this.valueField]){
14670 option['selected'] = true;
14673 var opt = this.inputEl().createChild(option);
14675 this.ios_options.push({
14682 this.inputEl().on('change', function(){
14683 this.fireEvent('select', this);
14688 clearIOSView: function()
14690 this.inputEl().dom.innerHTML = '';
14692 this.ios_options = [];
14695 setIOSValue: function(v)
14699 if(!this.ios_options){
14703 Roo.each(this.ios_options, function(opts){
14705 opts.el.dom.removeAttribute('selected');
14707 if(opts.data[this.valueField] != v){
14711 opts.el.dom.setAttribute('selected', true);
14717 * @cfg {Boolean} grow
14721 * @cfg {Number} growMin
14725 * @cfg {Number} growMax
14734 Roo.apply(Roo.bootstrap.ComboBox, {
14738 cls: 'modal-header',
14760 cls: 'list-group-item',
14764 cls: 'roo-combobox-list-group-item-value'
14768 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14782 listItemCheckbox : {
14784 cls: 'list-group-item',
14788 cls: 'roo-combobox-list-group-item-value'
14792 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14808 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14813 cls: 'modal-footer',
14821 cls: 'col-xs-6 text-left',
14824 cls: 'btn btn-danger roo-touch-view-cancel',
14830 cls: 'col-xs-6 text-right',
14833 cls: 'btn btn-success roo-touch-view-ok',
14844 Roo.apply(Roo.bootstrap.ComboBox, {
14846 touchViewTemplate : {
14848 cls: 'modal fade roo-combobox-touch-view',
14852 cls: 'modal-dialog',
14853 style : 'position:fixed', // we have to fix position....
14857 cls: 'modal-content',
14859 Roo.bootstrap.ComboBox.header,
14860 Roo.bootstrap.ComboBox.body,
14861 Roo.bootstrap.ComboBox.footer
14870 * Ext JS Library 1.1.1
14871 * Copyright(c) 2006-2007, Ext JS, LLC.
14873 * Originally Released Under LGPL - original licence link has changed is not relivant.
14876 * <script type="text/javascript">
14881 * @extends Roo.util.Observable
14882 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14883 * This class also supports single and multi selection modes. <br>
14884 * Create a data model bound view:
14886 var store = new Roo.data.Store(...);
14888 var view = new Roo.View({
14890 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14892 singleSelect: true,
14893 selectedClass: "ydataview-selected",
14897 // listen for node click?
14898 view.on("click", function(vw, index, node, e){
14899 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14903 dataModel.load("foobar.xml");
14905 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14907 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14908 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14910 * Note: old style constructor is still suported (container, template, config)
14913 * Create a new View
14914 * @param {Object} config The config object
14917 Roo.View = function(config, depreciated_tpl, depreciated_config){
14919 this.parent = false;
14921 if (typeof(depreciated_tpl) == 'undefined') {
14922 // new way.. - universal constructor.
14923 Roo.apply(this, config);
14924 this.el = Roo.get(this.el);
14927 this.el = Roo.get(config);
14928 this.tpl = depreciated_tpl;
14929 Roo.apply(this, depreciated_config);
14931 this.wrapEl = this.el.wrap().wrap();
14932 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14935 if(typeof(this.tpl) == "string"){
14936 this.tpl = new Roo.Template(this.tpl);
14938 // support xtype ctors..
14939 this.tpl = new Roo.factory(this.tpl, Roo);
14943 this.tpl.compile();
14948 * @event beforeclick
14949 * Fires before a click is processed. Returns false to cancel the default action.
14950 * @param {Roo.View} this
14951 * @param {Number} index The index of the target node
14952 * @param {HTMLElement} node The target node
14953 * @param {Roo.EventObject} e The raw event object
14955 "beforeclick" : true,
14958 * Fires when a template node is clicked.
14959 * @param {Roo.View} this
14960 * @param {Number} index The index of the target node
14961 * @param {HTMLElement} node The target node
14962 * @param {Roo.EventObject} e The raw event object
14967 * Fires when a template node is double clicked.
14968 * @param {Roo.View} this
14969 * @param {Number} index The index of the target node
14970 * @param {HTMLElement} node The target node
14971 * @param {Roo.EventObject} e The raw event object
14975 * @event contextmenu
14976 * Fires when a template node is right clicked.
14977 * @param {Roo.View} this
14978 * @param {Number} index The index of the target node
14979 * @param {HTMLElement} node The target node
14980 * @param {Roo.EventObject} e The raw event object
14982 "contextmenu" : true,
14984 * @event selectionchange
14985 * Fires when the selected nodes change.
14986 * @param {Roo.View} this
14987 * @param {Array} selections Array of the selected nodes
14989 "selectionchange" : true,
14992 * @event beforeselect
14993 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14994 * @param {Roo.View} this
14995 * @param {HTMLElement} node The node to be selected
14996 * @param {Array} selections Array of currently selected nodes
14998 "beforeselect" : true,
15000 * @event preparedata
15001 * Fires on every row to render, to allow you to change the data.
15002 * @param {Roo.View} this
15003 * @param {Object} data to be rendered (change this)
15005 "preparedata" : true
15013 "click": this.onClick,
15014 "dblclick": this.onDblClick,
15015 "contextmenu": this.onContextMenu,
15019 this.selections = [];
15021 this.cmp = new Roo.CompositeElementLite([]);
15023 this.store = Roo.factory(this.store, Roo.data);
15024 this.setStore(this.store, true);
15027 if ( this.footer && this.footer.xtype) {
15029 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15031 this.footer.dataSource = this.store;
15032 this.footer.container = fctr;
15033 this.footer = Roo.factory(this.footer, Roo);
15034 fctr.insertFirst(this.el);
15036 // this is a bit insane - as the paging toolbar seems to detach the el..
15037 // dom.parentNode.parentNode.parentNode
15038 // they get detached?
15042 Roo.View.superclass.constructor.call(this);
15047 Roo.extend(Roo.View, Roo.util.Observable, {
15050 * @cfg {Roo.data.Store} store Data store to load data from.
15055 * @cfg {String|Roo.Element} el The container element.
15060 * @cfg {String|Roo.Template} tpl The template used by this View
15064 * @cfg {String} dataName the named area of the template to use as the data area
15065 * Works with domtemplates roo-name="name"
15069 * @cfg {String} selectedClass The css class to add to selected nodes
15071 selectedClass : "x-view-selected",
15073 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15078 * @cfg {String} text to display on mask (default Loading)
15082 * @cfg {Boolean} multiSelect Allow multiple selection
15084 multiSelect : false,
15086 * @cfg {Boolean} singleSelect Allow single selection
15088 singleSelect: false,
15091 * @cfg {Boolean} toggleSelect - selecting
15093 toggleSelect : false,
15096 * @cfg {Boolean} tickable - selecting
15101 * Returns the element this view is bound to.
15102 * @return {Roo.Element}
15104 getEl : function(){
15105 return this.wrapEl;
15111 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15113 refresh : function(){
15114 //Roo.log('refresh');
15117 // if we are using something like 'domtemplate', then
15118 // the what gets used is:
15119 // t.applySubtemplate(NAME, data, wrapping data..)
15120 // the outer template then get' applied with
15121 // the store 'extra data'
15122 // and the body get's added to the
15123 // roo-name="data" node?
15124 // <span class='roo-tpl-{name}'></span> ?????
15128 this.clearSelections();
15129 this.el.update("");
15131 var records = this.store.getRange();
15132 if(records.length < 1) {
15134 // is this valid?? = should it render a template??
15136 this.el.update(this.emptyText);
15140 if (this.dataName) {
15141 this.el.update(t.apply(this.store.meta)); //????
15142 el = this.el.child('.roo-tpl-' + this.dataName);
15145 for(var i = 0, len = records.length; i < len; i++){
15146 var data = this.prepareData(records[i].data, i, records[i]);
15147 this.fireEvent("preparedata", this, data, i, records[i]);
15149 var d = Roo.apply({}, data);
15152 Roo.apply(d, {'roo-id' : Roo.id()});
15156 Roo.each(this.parent.item, function(item){
15157 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15160 Roo.apply(d, {'roo-data-checked' : 'checked'});
15164 html[html.length] = Roo.util.Format.trim(
15166 t.applySubtemplate(this.dataName, d, this.store.meta) :
15173 el.update(html.join(""));
15174 this.nodes = el.dom.childNodes;
15175 this.updateIndexes(0);
15180 * Function to override to reformat the data that is sent to
15181 * the template for each node.
15182 * DEPRICATED - use the preparedata event handler.
15183 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15184 * a JSON object for an UpdateManager bound view).
15186 prepareData : function(data, index, record)
15188 this.fireEvent("preparedata", this, data, index, record);
15192 onUpdate : function(ds, record){
15193 // Roo.log('on update');
15194 this.clearSelections();
15195 var index = this.store.indexOf(record);
15196 var n = this.nodes[index];
15197 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15198 n.parentNode.removeChild(n);
15199 this.updateIndexes(index, index);
15205 onAdd : function(ds, records, index)
15207 //Roo.log(['on Add', ds, records, index] );
15208 this.clearSelections();
15209 if(this.nodes.length == 0){
15213 var n = this.nodes[index];
15214 for(var i = 0, len = records.length; i < len; i++){
15215 var d = this.prepareData(records[i].data, i, records[i]);
15217 this.tpl.insertBefore(n, d);
15220 this.tpl.append(this.el, d);
15223 this.updateIndexes(index);
15226 onRemove : function(ds, record, index){
15227 // Roo.log('onRemove');
15228 this.clearSelections();
15229 var el = this.dataName ?
15230 this.el.child('.roo-tpl-' + this.dataName) :
15233 el.dom.removeChild(this.nodes[index]);
15234 this.updateIndexes(index);
15238 * Refresh an individual node.
15239 * @param {Number} index
15241 refreshNode : function(index){
15242 this.onUpdate(this.store, this.store.getAt(index));
15245 updateIndexes : function(startIndex, endIndex){
15246 var ns = this.nodes;
15247 startIndex = startIndex || 0;
15248 endIndex = endIndex || ns.length - 1;
15249 for(var i = startIndex; i <= endIndex; i++){
15250 ns[i].nodeIndex = i;
15255 * Changes the data store this view uses and refresh the view.
15256 * @param {Store} store
15258 setStore : function(store, initial){
15259 if(!initial && this.store){
15260 this.store.un("datachanged", this.refresh);
15261 this.store.un("add", this.onAdd);
15262 this.store.un("remove", this.onRemove);
15263 this.store.un("update", this.onUpdate);
15264 this.store.un("clear", this.refresh);
15265 this.store.un("beforeload", this.onBeforeLoad);
15266 this.store.un("load", this.onLoad);
15267 this.store.un("loadexception", this.onLoad);
15271 store.on("datachanged", this.refresh, this);
15272 store.on("add", this.onAdd, this);
15273 store.on("remove", this.onRemove, this);
15274 store.on("update", this.onUpdate, this);
15275 store.on("clear", this.refresh, this);
15276 store.on("beforeload", this.onBeforeLoad, this);
15277 store.on("load", this.onLoad, this);
15278 store.on("loadexception", this.onLoad, this);
15286 * onbeforeLoad - masks the loading area.
15289 onBeforeLoad : function(store,opts)
15291 //Roo.log('onBeforeLoad');
15293 this.el.update("");
15295 this.el.mask(this.mask ? this.mask : "Loading" );
15297 onLoad : function ()
15304 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15305 * @param {HTMLElement} node
15306 * @return {HTMLElement} The template node
15308 findItemFromChild : function(node){
15309 var el = this.dataName ?
15310 this.el.child('.roo-tpl-' + this.dataName,true) :
15313 if(!node || node.parentNode == el){
15316 var p = node.parentNode;
15317 while(p && p != el){
15318 if(p.parentNode == el){
15327 onClick : function(e){
15328 var item = this.findItemFromChild(e.getTarget());
15330 var index = this.indexOf(item);
15331 if(this.onItemClick(item, index, e) !== false){
15332 this.fireEvent("click", this, index, item, e);
15335 this.clearSelections();
15340 onContextMenu : function(e){
15341 var item = this.findItemFromChild(e.getTarget());
15343 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15348 onDblClick : function(e){
15349 var item = this.findItemFromChild(e.getTarget());
15351 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15355 onItemClick : function(item, index, e)
15357 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15360 if (this.toggleSelect) {
15361 var m = this.isSelected(item) ? 'unselect' : 'select';
15364 _t[m](item, true, false);
15367 if(this.multiSelect || this.singleSelect){
15368 if(this.multiSelect && e.shiftKey && this.lastSelection){
15369 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15371 this.select(item, this.multiSelect && e.ctrlKey);
15372 this.lastSelection = item;
15375 if(!this.tickable){
15376 e.preventDefault();
15384 * Get the number of selected nodes.
15387 getSelectionCount : function(){
15388 return this.selections.length;
15392 * Get the currently selected nodes.
15393 * @return {Array} An array of HTMLElements
15395 getSelectedNodes : function(){
15396 return this.selections;
15400 * Get the indexes of the selected nodes.
15403 getSelectedIndexes : function(){
15404 var indexes = [], s = this.selections;
15405 for(var i = 0, len = s.length; i < len; i++){
15406 indexes.push(s[i].nodeIndex);
15412 * Clear all selections
15413 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15415 clearSelections : function(suppressEvent){
15416 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15417 this.cmp.elements = this.selections;
15418 this.cmp.removeClass(this.selectedClass);
15419 this.selections = [];
15420 if(!suppressEvent){
15421 this.fireEvent("selectionchange", this, this.selections);
15427 * Returns true if the passed node is selected
15428 * @param {HTMLElement/Number} node The node or node index
15429 * @return {Boolean}
15431 isSelected : function(node){
15432 var s = this.selections;
15436 node = this.getNode(node);
15437 return s.indexOf(node) !== -1;
15442 * @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
15443 * @param {Boolean} keepExisting (optional) true to keep existing selections
15444 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15446 select : function(nodeInfo, keepExisting, suppressEvent){
15447 if(nodeInfo instanceof Array){
15449 this.clearSelections(true);
15451 for(var i = 0, len = nodeInfo.length; i < len; i++){
15452 this.select(nodeInfo[i], true, true);
15456 var node = this.getNode(nodeInfo);
15457 if(!node || this.isSelected(node)){
15458 return; // already selected.
15461 this.clearSelections(true);
15464 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15465 Roo.fly(node).addClass(this.selectedClass);
15466 this.selections.push(node);
15467 if(!suppressEvent){
15468 this.fireEvent("selectionchange", this, this.selections);
15476 * @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
15477 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15478 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15480 unselect : function(nodeInfo, keepExisting, suppressEvent)
15482 if(nodeInfo instanceof Array){
15483 Roo.each(this.selections, function(s) {
15484 this.unselect(s, nodeInfo);
15488 var node = this.getNode(nodeInfo);
15489 if(!node || !this.isSelected(node)){
15490 //Roo.log("not selected");
15491 return; // not selected.
15495 Roo.each(this.selections, function(s) {
15497 Roo.fly(node).removeClass(this.selectedClass);
15504 this.selections= ns;
15505 this.fireEvent("selectionchange", this, this.selections);
15509 * Gets a template node.
15510 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15511 * @return {HTMLElement} The node or null if it wasn't found
15513 getNode : function(nodeInfo){
15514 if(typeof nodeInfo == "string"){
15515 return document.getElementById(nodeInfo);
15516 }else if(typeof nodeInfo == "number"){
15517 return this.nodes[nodeInfo];
15523 * Gets a range template nodes.
15524 * @param {Number} startIndex
15525 * @param {Number} endIndex
15526 * @return {Array} An array of nodes
15528 getNodes : function(start, end){
15529 var ns = this.nodes;
15530 start = start || 0;
15531 end = typeof end == "undefined" ? ns.length - 1 : end;
15534 for(var i = start; i <= end; i++){
15538 for(var i = start; i >= end; i--){
15546 * Finds the index of the passed node
15547 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15548 * @return {Number} The index of the node or -1
15550 indexOf : function(node){
15551 node = this.getNode(node);
15552 if(typeof node.nodeIndex == "number"){
15553 return node.nodeIndex;
15555 var ns = this.nodes;
15556 for(var i = 0, len = ns.length; i < len; i++){
15567 * based on jquery fullcalendar
15571 Roo.bootstrap = Roo.bootstrap || {};
15573 * @class Roo.bootstrap.Calendar
15574 * @extends Roo.bootstrap.Component
15575 * Bootstrap Calendar class
15576 * @cfg {Boolean} loadMask (true|false) default false
15577 * @cfg {Object} header generate the user specific header of the calendar, default false
15580 * Create a new Container
15581 * @param {Object} config The config object
15586 Roo.bootstrap.Calendar = function(config){
15587 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15591 * Fires when a date is selected
15592 * @param {DatePicker} this
15593 * @param {Date} date The selected date
15597 * @event monthchange
15598 * Fires when the displayed month changes
15599 * @param {DatePicker} this
15600 * @param {Date} date The selected month
15602 'monthchange': true,
15604 * @event evententer
15605 * Fires when mouse over an event
15606 * @param {Calendar} this
15607 * @param {event} Event
15609 'evententer': true,
15611 * @event eventleave
15612 * Fires when the mouse leaves an
15613 * @param {Calendar} this
15616 'eventleave': true,
15618 * @event eventclick
15619 * Fires when the mouse click an
15620 * @param {Calendar} this
15629 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15632 * @cfg {Number} startDay
15633 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15641 getAutoCreate : function(){
15644 var fc_button = function(name, corner, style, content ) {
15645 return Roo.apply({},{
15647 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15649 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15652 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15663 style : 'width:100%',
15670 cls : 'fc-header-left',
15672 fc_button('prev', 'left', 'arrow', '‹' ),
15673 fc_button('next', 'right', 'arrow', '›' ),
15674 { tag: 'span', cls: 'fc-header-space' },
15675 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15683 cls : 'fc-header-center',
15687 cls: 'fc-header-title',
15690 html : 'month / year'
15698 cls : 'fc-header-right',
15700 /* fc_button('month', 'left', '', 'month' ),
15701 fc_button('week', '', '', 'week' ),
15702 fc_button('day', 'right', '', 'day' )
15714 header = this.header;
15717 var cal_heads = function() {
15719 // fixme - handle this.
15721 for (var i =0; i < Date.dayNames.length; i++) {
15722 var d = Date.dayNames[i];
15725 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15726 html : d.substring(0,3)
15730 ret[0].cls += ' fc-first';
15731 ret[6].cls += ' fc-last';
15734 var cal_cell = function(n) {
15737 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15742 cls: 'fc-day-number',
15746 cls: 'fc-day-content',
15750 style: 'position: relative;' // height: 17px;
15762 var cal_rows = function() {
15765 for (var r = 0; r < 6; r++) {
15772 for (var i =0; i < Date.dayNames.length; i++) {
15773 var d = Date.dayNames[i];
15774 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15777 row.cn[0].cls+=' fc-first';
15778 row.cn[0].cn[0].style = 'min-height:90px';
15779 row.cn[6].cls+=' fc-last';
15783 ret[0].cls += ' fc-first';
15784 ret[4].cls += ' fc-prev-last';
15785 ret[5].cls += ' fc-last';
15792 cls: 'fc-border-separate',
15793 style : 'width:100%',
15801 cls : 'fc-first fc-last',
15819 cls : 'fc-content',
15820 style : "position: relative;",
15823 cls : 'fc-view fc-view-month fc-grid',
15824 style : 'position: relative',
15825 unselectable : 'on',
15828 cls : 'fc-event-container',
15829 style : 'position:absolute;z-index:8;top:0;left:0;'
15847 initEvents : function()
15850 throw "can not find store for calendar";
15856 style: "text-align:center",
15860 style: "background-color:white;width:50%;margin:250 auto",
15864 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15875 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15877 var size = this.el.select('.fc-content', true).first().getSize();
15878 this.maskEl.setSize(size.width, size.height);
15879 this.maskEl.enableDisplayMode("block");
15880 if(!this.loadMask){
15881 this.maskEl.hide();
15884 this.store = Roo.factory(this.store, Roo.data);
15885 this.store.on('load', this.onLoad, this);
15886 this.store.on('beforeload', this.onBeforeLoad, this);
15890 this.cells = this.el.select('.fc-day',true);
15891 //Roo.log(this.cells);
15892 this.textNodes = this.el.query('.fc-day-number');
15893 this.cells.addClassOnOver('fc-state-hover');
15895 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15896 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15897 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15898 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15900 this.on('monthchange', this.onMonthChange, this);
15902 this.update(new Date().clearTime());
15905 resize : function() {
15906 var sz = this.el.getSize();
15908 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15909 this.el.select('.fc-day-content div',true).setHeight(34);
15914 showPrevMonth : function(e){
15915 this.update(this.activeDate.add("mo", -1));
15917 showToday : function(e){
15918 this.update(new Date().clearTime());
15921 showNextMonth : function(e){
15922 this.update(this.activeDate.add("mo", 1));
15926 showPrevYear : function(){
15927 this.update(this.activeDate.add("y", -1));
15931 showNextYear : function(){
15932 this.update(this.activeDate.add("y", 1));
15937 update : function(date)
15939 var vd = this.activeDate;
15940 this.activeDate = date;
15941 // if(vd && this.el){
15942 // var t = date.getTime();
15943 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15944 // Roo.log('using add remove');
15946 // this.fireEvent('monthchange', this, date);
15948 // this.cells.removeClass("fc-state-highlight");
15949 // this.cells.each(function(c){
15950 // if(c.dateValue == t){
15951 // c.addClass("fc-state-highlight");
15952 // setTimeout(function(){
15953 // try{c.dom.firstChild.focus();}catch(e){}
15963 var days = date.getDaysInMonth();
15965 var firstOfMonth = date.getFirstDateOfMonth();
15966 var startingPos = firstOfMonth.getDay()-this.startDay;
15968 if(startingPos < this.startDay){
15972 var pm = date.add(Date.MONTH, -1);
15973 var prevStart = pm.getDaysInMonth()-startingPos;
15975 this.cells = this.el.select('.fc-day',true);
15976 this.textNodes = this.el.query('.fc-day-number');
15977 this.cells.addClassOnOver('fc-state-hover');
15979 var cells = this.cells.elements;
15980 var textEls = this.textNodes;
15982 Roo.each(cells, function(cell){
15983 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15986 days += startingPos;
15988 // convert everything to numbers so it's fast
15989 var day = 86400000;
15990 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15993 //Roo.log(prevStart);
15995 var today = new Date().clearTime().getTime();
15996 var sel = date.clearTime().getTime();
15997 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15998 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15999 var ddMatch = this.disabledDatesRE;
16000 var ddText = this.disabledDatesText;
16001 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16002 var ddaysText = this.disabledDaysText;
16003 var format = this.format;
16005 var setCellClass = function(cal, cell){
16009 //Roo.log('set Cell Class');
16011 var t = d.getTime();
16015 cell.dateValue = t;
16017 cell.className += " fc-today";
16018 cell.className += " fc-state-highlight";
16019 cell.title = cal.todayText;
16022 // disable highlight in other month..
16023 //cell.className += " fc-state-highlight";
16028 cell.className = " fc-state-disabled";
16029 cell.title = cal.minText;
16033 cell.className = " fc-state-disabled";
16034 cell.title = cal.maxText;
16038 if(ddays.indexOf(d.getDay()) != -1){
16039 cell.title = ddaysText;
16040 cell.className = " fc-state-disabled";
16043 if(ddMatch && format){
16044 var fvalue = d.dateFormat(format);
16045 if(ddMatch.test(fvalue)){
16046 cell.title = ddText.replace("%0", fvalue);
16047 cell.className = " fc-state-disabled";
16051 if (!cell.initialClassName) {
16052 cell.initialClassName = cell.dom.className;
16055 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16060 for(; i < startingPos; i++) {
16061 textEls[i].innerHTML = (++prevStart);
16062 d.setDate(d.getDate()+1);
16064 cells[i].className = "fc-past fc-other-month";
16065 setCellClass(this, cells[i]);
16070 for(; i < days; i++){
16071 intDay = i - startingPos + 1;
16072 textEls[i].innerHTML = (intDay);
16073 d.setDate(d.getDate()+1);
16075 cells[i].className = ''; // "x-date-active";
16076 setCellClass(this, cells[i]);
16080 for(; i < 42; i++) {
16081 textEls[i].innerHTML = (++extraDays);
16082 d.setDate(d.getDate()+1);
16084 cells[i].className = "fc-future fc-other-month";
16085 setCellClass(this, cells[i]);
16088 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16090 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16092 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16093 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16095 if(totalRows != 6){
16096 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16097 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16100 this.fireEvent('monthchange', this, date);
16104 if(!this.internalRender){
16105 var main = this.el.dom.firstChild;
16106 var w = main.offsetWidth;
16107 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16108 Roo.fly(main).setWidth(w);
16109 this.internalRender = true;
16110 // opera does not respect the auto grow header center column
16111 // then, after it gets a width opera refuses to recalculate
16112 // without a second pass
16113 if(Roo.isOpera && !this.secondPass){
16114 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16115 this.secondPass = true;
16116 this.update.defer(10, this, [date]);
16123 findCell : function(dt) {
16124 dt = dt.clearTime().getTime();
16126 this.cells.each(function(c){
16127 //Roo.log("check " +c.dateValue + '?=' + dt);
16128 if(c.dateValue == dt){
16138 findCells : function(ev) {
16139 var s = ev.start.clone().clearTime().getTime();
16141 var e= ev.end.clone().clearTime().getTime();
16144 this.cells.each(function(c){
16145 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16147 if(c.dateValue > e){
16150 if(c.dateValue < s){
16159 // findBestRow: function(cells)
16163 // for (var i =0 ; i < cells.length;i++) {
16164 // ret = Math.max(cells[i].rows || 0,ret);
16171 addItem : function(ev)
16173 // look for vertical location slot in
16174 var cells = this.findCells(ev);
16176 // ev.row = this.findBestRow(cells);
16178 // work out the location.
16182 for(var i =0; i < cells.length; i++) {
16184 cells[i].row = cells[0].row;
16187 cells[i].row = cells[i].row + 1;
16197 if (crow.start.getY() == cells[i].getY()) {
16199 crow.end = cells[i];
16216 cells[0].events.push(ev);
16218 this.calevents.push(ev);
16221 clearEvents: function() {
16223 if(!this.calevents){
16227 Roo.each(this.cells.elements, function(c){
16233 Roo.each(this.calevents, function(e) {
16234 Roo.each(e.els, function(el) {
16235 el.un('mouseenter' ,this.onEventEnter, this);
16236 el.un('mouseleave' ,this.onEventLeave, this);
16241 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16247 renderEvents: function()
16251 this.cells.each(function(c) {
16260 if(c.row != c.events.length){
16261 r = 4 - (4 - (c.row - c.events.length));
16264 c.events = ev.slice(0, r);
16265 c.more = ev.slice(r);
16267 if(c.more.length && c.more.length == 1){
16268 c.events.push(c.more.pop());
16271 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16275 this.cells.each(function(c) {
16277 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16280 for (var e = 0; e < c.events.length; e++){
16281 var ev = c.events[e];
16282 var rows = ev.rows;
16284 for(var i = 0; i < rows.length; i++) {
16286 // how many rows should it span..
16289 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16290 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16292 unselectable : "on",
16295 cls: 'fc-event-inner',
16299 // cls: 'fc-event-time',
16300 // html : cells.length > 1 ? '' : ev.time
16304 cls: 'fc-event-title',
16305 html : String.format('{0}', ev.title)
16312 cls: 'ui-resizable-handle ui-resizable-e',
16313 html : '  '
16320 cfg.cls += ' fc-event-start';
16322 if ((i+1) == rows.length) {
16323 cfg.cls += ' fc-event-end';
16326 var ctr = _this.el.select('.fc-event-container',true).first();
16327 var cg = ctr.createChild(cfg);
16329 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16330 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16332 var r = (c.more.length) ? 1 : 0;
16333 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16334 cg.setWidth(ebox.right - sbox.x -2);
16336 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16337 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16338 cg.on('click', _this.onEventClick, _this, ev);
16349 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16350 style : 'position: absolute',
16351 unselectable : "on",
16354 cls: 'fc-event-inner',
16358 cls: 'fc-event-title',
16366 cls: 'ui-resizable-handle ui-resizable-e',
16367 html : '  '
16373 var ctr = _this.el.select('.fc-event-container',true).first();
16374 var cg = ctr.createChild(cfg);
16376 var sbox = c.select('.fc-day-content',true).first().getBox();
16377 var ebox = c.select('.fc-day-content',true).first().getBox();
16379 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16380 cg.setWidth(ebox.right - sbox.x -2);
16382 cg.on('click', _this.onMoreEventClick, _this, c.more);
16392 onEventEnter: function (e, el,event,d) {
16393 this.fireEvent('evententer', this, el, event);
16396 onEventLeave: function (e, el,event,d) {
16397 this.fireEvent('eventleave', this, el, event);
16400 onEventClick: function (e, el,event,d) {
16401 this.fireEvent('eventclick', this, el, event);
16404 onMonthChange: function () {
16408 onMoreEventClick: function(e, el, more)
16412 this.calpopover.placement = 'right';
16413 this.calpopover.setTitle('More');
16415 this.calpopover.setContent('');
16417 var ctr = this.calpopover.el.select('.popover-content', true).first();
16419 Roo.each(more, function(m){
16421 cls : 'fc-event-hori fc-event-draggable',
16424 var cg = ctr.createChild(cfg);
16426 cg.on('click', _this.onEventClick, _this, m);
16429 this.calpopover.show(el);
16434 onLoad: function ()
16436 this.calevents = [];
16439 if(this.store.getCount() > 0){
16440 this.store.data.each(function(d){
16443 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16444 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16445 time : d.data.start_time,
16446 title : d.data.title,
16447 description : d.data.description,
16448 venue : d.data.venue
16453 this.renderEvents();
16455 if(this.calevents.length && this.loadMask){
16456 this.maskEl.hide();
16460 onBeforeLoad: function()
16462 this.clearEvents();
16464 this.maskEl.show();
16478 * @class Roo.bootstrap.Popover
16479 * @extends Roo.bootstrap.Component
16480 * Bootstrap Popover class
16481 * @cfg {String} html contents of the popover (or false to use children..)
16482 * @cfg {String} title of popover (or false to hide)
16483 * @cfg {String} placement how it is placed
16484 * @cfg {String} trigger click || hover (or false to trigger manually)
16485 * @cfg {String} over what (parent or false to trigger manually.)
16486 * @cfg {Number} delay - delay before showing
16489 * Create a new Popover
16490 * @param {Object} config The config object
16493 Roo.bootstrap.Popover = function(config){
16494 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16500 * After the popover show
16502 * @param {Roo.bootstrap.Popover} this
16507 * After the popover hide
16509 * @param {Roo.bootstrap.Popover} this
16515 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16517 title: 'Fill in a title',
16520 placement : 'right',
16521 trigger : 'hover', // hover
16527 can_build_overlaid : false,
16529 getChildContainer : function()
16531 return this.el.select('.popover-content',true).first();
16534 getAutoCreate : function(){
16537 cls : 'popover roo-dynamic',
16538 style: 'display:block',
16544 cls : 'popover-inner',
16548 cls: 'popover-title',
16552 cls : 'popover-content',
16563 setTitle: function(str)
16566 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16568 setContent: function(str)
16571 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16573 // as it get's added to the bottom of the page.
16574 onRender : function(ct, position)
16576 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16578 var cfg = Roo.apply({}, this.getAutoCreate());
16582 cfg.cls += ' ' + this.cls;
16585 cfg.style = this.style;
16587 //Roo.log("adding to ");
16588 this.el = Roo.get(document.body).createChild(cfg, position);
16589 // Roo.log(this.el);
16594 initEvents : function()
16596 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16597 this.el.enableDisplayMode('block');
16599 if (this.over === false) {
16602 if (this.triggers === false) {
16605 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16606 var triggers = this.trigger ? this.trigger.split(' ') : [];
16607 Roo.each(triggers, function(trigger) {
16609 if (trigger == 'click') {
16610 on_el.on('click', this.toggle, this);
16611 } else if (trigger != 'manual') {
16612 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16613 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16615 on_el.on(eventIn ,this.enter, this);
16616 on_el.on(eventOut, this.leave, this);
16627 toggle : function () {
16628 this.hoverState == 'in' ? this.leave() : this.enter();
16631 enter : function () {
16633 clearTimeout(this.timeout);
16635 this.hoverState = 'in';
16637 if (!this.delay || !this.delay.show) {
16642 this.timeout = setTimeout(function () {
16643 if (_t.hoverState == 'in') {
16646 }, this.delay.show)
16649 leave : function() {
16650 clearTimeout(this.timeout);
16652 this.hoverState = 'out';
16654 if (!this.delay || !this.delay.hide) {
16659 this.timeout = setTimeout(function () {
16660 if (_t.hoverState == 'out') {
16663 }, this.delay.hide)
16666 show : function (on_el)
16669 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16673 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16674 if (this.html !== false) {
16675 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16677 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16678 if (!this.title.length) {
16679 this.el.select('.popover-title',true).hide();
16682 var placement = typeof this.placement == 'function' ?
16683 this.placement.call(this, this.el, on_el) :
16686 var autoToken = /\s?auto?\s?/i;
16687 var autoPlace = autoToken.test(placement);
16689 placement = placement.replace(autoToken, '') || 'top';
16693 //this.el.setXY([0,0]);
16695 this.el.dom.style.display='block';
16696 this.el.addClass(placement);
16698 //this.el.appendTo(on_el);
16700 var p = this.getPosition();
16701 var box = this.el.getBox();
16706 var align = Roo.bootstrap.Popover.alignment[placement];
16707 this.el.alignTo(on_el, align[0],align[1]);
16708 //var arrow = this.el.select('.arrow',true).first();
16709 //arrow.set(align[2],
16711 this.el.addClass('in');
16714 if (this.el.hasClass('fade')) {
16718 this.hoverState = 'in';
16720 this.fireEvent('show', this);
16725 this.el.setXY([0,0]);
16726 this.el.removeClass('in');
16728 this.hoverState = null;
16730 this.fireEvent('hide', this);
16735 Roo.bootstrap.Popover.alignment = {
16736 'left' : ['r-l', [-10,0], 'right'],
16737 'right' : ['l-r', [10,0], 'left'],
16738 'bottom' : ['t-b', [0,10], 'top'],
16739 'top' : [ 'b-t', [0,-10], 'bottom']
16750 * @class Roo.bootstrap.Progress
16751 * @extends Roo.bootstrap.Component
16752 * Bootstrap Progress class
16753 * @cfg {Boolean} striped striped of the progress bar
16754 * @cfg {Boolean} active animated of the progress bar
16758 * Create a new Progress
16759 * @param {Object} config The config object
16762 Roo.bootstrap.Progress = function(config){
16763 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16766 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16771 getAutoCreate : function(){
16779 cfg.cls += ' progress-striped';
16783 cfg.cls += ' active';
16802 * @class Roo.bootstrap.ProgressBar
16803 * @extends Roo.bootstrap.Component
16804 * Bootstrap ProgressBar class
16805 * @cfg {Number} aria_valuenow aria-value now
16806 * @cfg {Number} aria_valuemin aria-value min
16807 * @cfg {Number} aria_valuemax aria-value max
16808 * @cfg {String} label label for the progress bar
16809 * @cfg {String} panel (success | info | warning | danger )
16810 * @cfg {String} role role of the progress bar
16811 * @cfg {String} sr_only text
16815 * Create a new ProgressBar
16816 * @param {Object} config The config object
16819 Roo.bootstrap.ProgressBar = function(config){
16820 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16823 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16827 aria_valuemax : 100,
16833 getAutoCreate : function()
16838 cls: 'progress-bar',
16839 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16851 cfg.role = this.role;
16854 if(this.aria_valuenow){
16855 cfg['aria-valuenow'] = this.aria_valuenow;
16858 if(this.aria_valuemin){
16859 cfg['aria-valuemin'] = this.aria_valuemin;
16862 if(this.aria_valuemax){
16863 cfg['aria-valuemax'] = this.aria_valuemax;
16866 if(this.label && !this.sr_only){
16867 cfg.html = this.label;
16871 cfg.cls += ' progress-bar-' + this.panel;
16877 update : function(aria_valuenow)
16879 this.aria_valuenow = aria_valuenow;
16881 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16896 * @class Roo.bootstrap.TabGroup
16897 * @extends Roo.bootstrap.Column
16898 * Bootstrap Column class
16899 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16900 * @cfg {Boolean} carousel true to make the group behave like a carousel
16901 * @cfg {Boolean} bullets show bullets for the panels
16902 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16903 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16904 * @cfg {Boolean} showarrow (true|false) show arrow default true
16907 * Create a new TabGroup
16908 * @param {Object} config The config object
16911 Roo.bootstrap.TabGroup = function(config){
16912 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16914 this.navId = Roo.id();
16917 Roo.bootstrap.TabGroup.register(this);
16921 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16924 transition : false,
16929 slideOnTouch : false,
16932 getAutoCreate : function()
16934 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16936 cfg.cls += ' tab-content';
16938 if (this.carousel) {
16939 cfg.cls += ' carousel slide';
16942 cls : 'carousel-inner',
16946 if(this.bullets && !Roo.isTouch){
16949 cls : 'carousel-bullets',
16953 if(this.bullets_cls){
16954 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16961 cfg.cn[0].cn.push(bullets);
16964 if(this.showarrow){
16965 cfg.cn[0].cn.push({
16967 class : 'carousel-arrow',
16971 class : 'carousel-prev',
16975 class : 'fa fa-chevron-left'
16981 class : 'carousel-next',
16985 class : 'fa fa-chevron-right'
16998 initEvents: function()
17000 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17001 // this.el.on("touchstart", this.onTouchStart, this);
17004 if(this.autoslide){
17007 this.slideFn = window.setInterval(function() {
17008 _this.showPanelNext();
17012 if(this.showarrow){
17013 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17014 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17020 // onTouchStart : function(e, el, o)
17022 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17026 // this.showPanelNext();
17030 getChildContainer : function()
17032 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17036 * register a Navigation item
17037 * @param {Roo.bootstrap.NavItem} the navitem to add
17039 register : function(item)
17041 this.tabs.push( item);
17042 item.navId = this.navId; // not really needed..
17047 getActivePanel : function()
17050 Roo.each(this.tabs, function(t) {
17060 getPanelByName : function(n)
17063 Roo.each(this.tabs, function(t) {
17064 if (t.tabId == n) {
17072 indexOfPanel : function(p)
17075 Roo.each(this.tabs, function(t,i) {
17076 if (t.tabId == p.tabId) {
17085 * show a specific panel
17086 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17087 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17089 showPanel : function (pan)
17091 if(this.transition || typeof(pan) == 'undefined'){
17092 Roo.log("waiting for the transitionend");
17096 if (typeof(pan) == 'number') {
17097 pan = this.tabs[pan];
17100 if (typeof(pan) == 'string') {
17101 pan = this.getPanelByName(pan);
17104 var cur = this.getActivePanel();
17107 Roo.log('pan or acitve pan is undefined');
17111 if (pan.tabId == this.getActivePanel().tabId) {
17115 if (false === cur.fireEvent('beforedeactivate')) {
17119 if(this.bullets > 0 && !Roo.isTouch){
17120 this.setActiveBullet(this.indexOfPanel(pan));
17123 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17125 this.transition = true;
17126 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17127 var lr = dir == 'next' ? 'left' : 'right';
17128 pan.el.addClass(dir); // or prev
17129 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17130 cur.el.addClass(lr); // or right
17131 pan.el.addClass(lr);
17134 cur.el.on('transitionend', function() {
17135 Roo.log("trans end?");
17137 pan.el.removeClass([lr,dir]);
17138 pan.setActive(true);
17140 cur.el.removeClass([lr]);
17141 cur.setActive(false);
17143 _this.transition = false;
17145 }, this, { single: true } );
17150 cur.setActive(false);
17151 pan.setActive(true);
17156 showPanelNext : function()
17158 var i = this.indexOfPanel(this.getActivePanel());
17160 if (i >= this.tabs.length - 1 && !this.autoslide) {
17164 if (i >= this.tabs.length - 1 && this.autoslide) {
17168 this.showPanel(this.tabs[i+1]);
17171 showPanelPrev : function()
17173 var i = this.indexOfPanel(this.getActivePanel());
17175 if (i < 1 && !this.autoslide) {
17179 if (i < 1 && this.autoslide) {
17180 i = this.tabs.length;
17183 this.showPanel(this.tabs[i-1]);
17187 addBullet: function()
17189 if(!this.bullets || Roo.isTouch){
17192 var ctr = this.el.select('.carousel-bullets',true).first();
17193 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17194 var bullet = ctr.createChild({
17195 cls : 'bullet bullet-' + i
17196 },ctr.dom.lastChild);
17201 bullet.on('click', (function(e, el, o, ii, t){
17203 e.preventDefault();
17205 this.showPanel(ii);
17207 if(this.autoslide && this.slideFn){
17208 clearInterval(this.slideFn);
17209 this.slideFn = window.setInterval(function() {
17210 _this.showPanelNext();
17214 }).createDelegate(this, [i, bullet], true));
17219 setActiveBullet : function(i)
17225 Roo.each(this.el.select('.bullet', true).elements, function(el){
17226 el.removeClass('selected');
17229 var bullet = this.el.select('.bullet-' + i, true).first();
17235 bullet.addClass('selected');
17246 Roo.apply(Roo.bootstrap.TabGroup, {
17250 * register a Navigation Group
17251 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17253 register : function(navgrp)
17255 this.groups[navgrp.navId] = navgrp;
17259 * fetch a Navigation Group based on the navigation ID
17260 * if one does not exist , it will get created.
17261 * @param {string} the navgroup to add
17262 * @returns {Roo.bootstrap.NavGroup} the navgroup
17264 get: function(navId) {
17265 if (typeof(this.groups[navId]) == 'undefined') {
17266 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17268 return this.groups[navId] ;
17283 * @class Roo.bootstrap.TabPanel
17284 * @extends Roo.bootstrap.Component
17285 * Bootstrap TabPanel class
17286 * @cfg {Boolean} active panel active
17287 * @cfg {String} html panel content
17288 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17289 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17290 * @cfg {String} href click to link..
17294 * Create a new TabPanel
17295 * @param {Object} config The config object
17298 Roo.bootstrap.TabPanel = function(config){
17299 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17303 * Fires when the active status changes
17304 * @param {Roo.bootstrap.TabPanel} this
17305 * @param {Boolean} state the new state
17310 * @event beforedeactivate
17311 * Fires before a tab is de-activated - can be used to do validation on a form.
17312 * @param {Roo.bootstrap.TabPanel} this
17313 * @return {Boolean} false if there is an error
17316 'beforedeactivate': true
17319 this.tabId = this.tabId || Roo.id();
17323 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17331 getAutoCreate : function(){
17334 // item is needed for carousel - not sure if it has any effect otherwise
17335 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17336 html: this.html || ''
17340 cfg.cls += ' active';
17344 cfg.tabId = this.tabId;
17351 initEvents: function()
17353 var p = this.parent();
17355 this.navId = this.navId || p.navId;
17357 if (typeof(this.navId) != 'undefined') {
17358 // not really needed.. but just in case.. parent should be a NavGroup.
17359 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17363 var i = tg.tabs.length - 1;
17365 if(this.active && tg.bullets > 0 && i < tg.bullets){
17366 tg.setActiveBullet(i);
17370 this.el.on('click', this.onClick, this);
17373 this.el.on("touchstart", this.onTouchStart, this);
17374 this.el.on("touchmove", this.onTouchMove, this);
17375 this.el.on("touchend", this.onTouchEnd, this);
17380 onRender : function(ct, position)
17382 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17385 setActive : function(state)
17387 Roo.log("panel - set active " + this.tabId + "=" + state);
17389 this.active = state;
17391 this.el.removeClass('active');
17393 } else if (!this.el.hasClass('active')) {
17394 this.el.addClass('active');
17397 this.fireEvent('changed', this, state);
17400 onClick : function(e)
17402 e.preventDefault();
17404 if(!this.href.length){
17408 window.location.href = this.href;
17417 onTouchStart : function(e)
17419 this.swiping = false;
17421 this.startX = e.browserEvent.touches[0].clientX;
17422 this.startY = e.browserEvent.touches[0].clientY;
17425 onTouchMove : function(e)
17427 this.swiping = true;
17429 this.endX = e.browserEvent.touches[0].clientX;
17430 this.endY = e.browserEvent.touches[0].clientY;
17433 onTouchEnd : function(e)
17440 var tabGroup = this.parent();
17442 if(this.endX > this.startX){ // swiping right
17443 tabGroup.showPanelPrev();
17447 if(this.startX > this.endX){ // swiping left
17448 tabGroup.showPanelNext();
17467 * @class Roo.bootstrap.DateField
17468 * @extends Roo.bootstrap.Input
17469 * Bootstrap DateField class
17470 * @cfg {Number} weekStart default 0
17471 * @cfg {String} viewMode default empty, (months|years)
17472 * @cfg {String} minViewMode default empty, (months|years)
17473 * @cfg {Number} startDate default -Infinity
17474 * @cfg {Number} endDate default Infinity
17475 * @cfg {Boolean} todayHighlight default false
17476 * @cfg {Boolean} todayBtn default false
17477 * @cfg {Boolean} calendarWeeks default false
17478 * @cfg {Object} daysOfWeekDisabled default empty
17479 * @cfg {Boolean} singleMode default false (true | false)
17481 * @cfg {Boolean} keyboardNavigation default true
17482 * @cfg {String} language default en
17485 * Create a new DateField
17486 * @param {Object} config The config object
17489 Roo.bootstrap.DateField = function(config){
17490 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17494 * Fires when this field show.
17495 * @param {Roo.bootstrap.DateField} this
17496 * @param {Mixed} date The date value
17501 * Fires when this field hide.
17502 * @param {Roo.bootstrap.DateField} this
17503 * @param {Mixed} date The date value
17508 * Fires when select a date.
17509 * @param {Roo.bootstrap.DateField} this
17510 * @param {Mixed} date The date value
17514 * @event beforeselect
17515 * Fires when before select a date.
17516 * @param {Roo.bootstrap.DateField} this
17517 * @param {Mixed} date The date value
17519 beforeselect : true
17523 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
17526 * @cfg {String} format
17527 * The default date format string which can be overriden for localization support. The format must be
17528 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17532 * @cfg {String} altFormats
17533 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17534 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17536 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17544 todayHighlight : false,
17550 keyboardNavigation: true,
17552 calendarWeeks: false,
17554 startDate: -Infinity,
17558 daysOfWeekDisabled: [],
17562 singleMode : false,
17564 UTCDate: function()
17566 return new Date(Date.UTC.apply(Date, arguments));
17569 UTCToday: function()
17571 var today = new Date();
17572 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17575 getDate: function() {
17576 var d = this.getUTCDate();
17577 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17580 getUTCDate: function() {
17584 setDate: function(d) {
17585 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17588 setUTCDate: function(d) {
17590 this.setValue(this.formatDate(this.date));
17593 onRender: function(ct, position)
17596 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17598 this.language = this.language || 'en';
17599 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17600 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17602 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17603 this.format = this.format || 'm/d/y';
17604 this.isInline = false;
17605 this.isInput = true;
17606 this.component = this.el.select('.add-on', true).first() || false;
17607 this.component = (this.component && this.component.length === 0) ? false : this.component;
17608 this.hasInput = this.component && this.inputEl().length;
17610 if (typeof(this.minViewMode === 'string')) {
17611 switch (this.minViewMode) {
17613 this.minViewMode = 1;
17616 this.minViewMode = 2;
17619 this.minViewMode = 0;
17624 if (typeof(this.viewMode === 'string')) {
17625 switch (this.viewMode) {
17638 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17640 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17642 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17644 this.picker().on('mousedown', this.onMousedown, this);
17645 this.picker().on('click', this.onClick, this);
17647 this.picker().addClass('datepicker-dropdown');
17649 this.startViewMode = this.viewMode;
17651 if(this.singleMode){
17652 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17653 v.setVisibilityMode(Roo.Element.DISPLAY);
17657 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17658 v.setStyle('width', '189px');
17662 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17663 if(!this.calendarWeeks){
17668 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17669 v.attr('colspan', function(i, val){
17670 return parseInt(val) + 1;
17675 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17677 this.setStartDate(this.startDate);
17678 this.setEndDate(this.endDate);
17680 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17687 if(this.isInline) {
17692 picker : function()
17694 return this.pickerEl;
17695 // return this.el.select('.datepicker', true).first();
17698 fillDow: function()
17700 var dowCnt = this.weekStart;
17709 if(this.calendarWeeks){
17717 while (dowCnt < this.weekStart + 7) {
17721 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17725 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17728 fillMonths: function()
17731 var months = this.picker().select('>.datepicker-months td', true).first();
17733 months.dom.innerHTML = '';
17739 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17742 months.createChild(month);
17749 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;
17751 if (this.date < this.startDate) {
17752 this.viewDate = new Date(this.startDate);
17753 } else if (this.date > this.endDate) {
17754 this.viewDate = new Date(this.endDate);
17756 this.viewDate = new Date(this.date);
17764 var d = new Date(this.viewDate),
17765 year = d.getUTCFullYear(),
17766 month = d.getUTCMonth(),
17767 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17768 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17769 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17770 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17771 currentDate = this.date && this.date.valueOf(),
17772 today = this.UTCToday();
17774 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17776 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17778 // this.picker.select('>tfoot th.today').
17779 // .text(dates[this.language].today)
17780 // .toggle(this.todayBtn !== false);
17782 this.updateNavArrows();
17785 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17787 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17789 prevMonth.setUTCDate(day);
17791 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17793 var nextMonth = new Date(prevMonth);
17795 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17797 nextMonth = nextMonth.valueOf();
17799 var fillMonths = false;
17801 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17803 while(prevMonth.valueOf() < nextMonth) {
17806 if (prevMonth.getUTCDay() === this.weekStart) {
17808 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17816 if(this.calendarWeeks){
17817 // ISO 8601: First week contains first thursday.
17818 // ISO also states week starts on Monday, but we can be more abstract here.
17820 // Start of current week: based on weekstart/current date
17821 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17822 // Thursday of this week
17823 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17824 // First Thursday of year, year from thursday
17825 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17826 // Calendar week: ms between thursdays, div ms per day, div 7 days
17827 calWeek = (th - yth) / 864e5 / 7 + 1;
17829 fillMonths.cn.push({
17837 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17839 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17842 if (this.todayHighlight &&
17843 prevMonth.getUTCFullYear() == today.getFullYear() &&
17844 prevMonth.getUTCMonth() == today.getMonth() &&
17845 prevMonth.getUTCDate() == today.getDate()) {
17846 clsName += ' today';
17849 if (currentDate && prevMonth.valueOf() === currentDate) {
17850 clsName += ' active';
17853 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17854 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17855 clsName += ' disabled';
17858 fillMonths.cn.push({
17860 cls: 'day ' + clsName,
17861 html: prevMonth.getDate()
17864 prevMonth.setDate(prevMonth.getDate()+1);
17867 var currentYear = this.date && this.date.getUTCFullYear();
17868 var currentMonth = this.date && this.date.getUTCMonth();
17870 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17872 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17873 v.removeClass('active');
17875 if(currentYear === year && k === currentMonth){
17876 v.addClass('active');
17879 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17880 v.addClass('disabled');
17886 year = parseInt(year/10, 10) * 10;
17888 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17890 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17893 for (var i = -1; i < 11; i++) {
17894 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17896 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17904 showMode: function(dir)
17907 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17910 Roo.each(this.picker().select('>div',true).elements, function(v){
17911 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17914 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17919 if(this.isInline) {
17923 this.picker().removeClass(['bottom', 'top']);
17925 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17927 * place to the top of element!
17931 this.picker().addClass('top');
17932 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17937 this.picker().addClass('bottom');
17939 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17942 parseDate : function(value)
17944 if(!value || value instanceof Date){
17947 var v = Date.parseDate(value, this.format);
17948 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17949 v = Date.parseDate(value, 'Y-m-d');
17951 if(!v && this.altFormats){
17952 if(!this.altFormatsArray){
17953 this.altFormatsArray = this.altFormats.split("|");
17955 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17956 v = Date.parseDate(value, this.altFormatsArray[i]);
17962 formatDate : function(date, fmt)
17964 return (!date || !(date instanceof Date)) ?
17965 date : date.dateFormat(fmt || this.format);
17968 onFocus : function()
17970 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17974 onBlur : function()
17976 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17978 var d = this.inputEl().getValue();
17987 this.picker().show();
17991 this.fireEvent('show', this, this.date);
17996 if(this.isInline) {
17999 this.picker().hide();
18000 this.viewMode = this.startViewMode;
18003 this.fireEvent('hide', this, this.date);
18007 onMousedown: function(e)
18009 e.stopPropagation();
18010 e.preventDefault();
18015 Roo.bootstrap.DateField.superclass.keyup.call(this);
18019 setValue: function(v)
18021 if(this.fireEvent('beforeselect', this, v) !== false){
18022 var d = new Date(this.parseDate(v) ).clearTime();
18024 if(isNaN(d.getTime())){
18025 this.date = this.viewDate = '';
18026 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18030 v = this.formatDate(d);
18032 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18034 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18038 this.fireEvent('select', this, this.date);
18042 getValue: function()
18044 return this.formatDate(this.date);
18047 fireKey: function(e)
18049 if (!this.picker().isVisible()){
18050 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18056 var dateChanged = false,
18058 newDate, newViewDate;
18063 e.preventDefault();
18067 if (!this.keyboardNavigation) {
18070 dir = e.keyCode == 37 ? -1 : 1;
18073 newDate = this.moveYear(this.date, dir);
18074 newViewDate = this.moveYear(this.viewDate, dir);
18075 } else if (e.shiftKey){
18076 newDate = this.moveMonth(this.date, dir);
18077 newViewDate = this.moveMonth(this.viewDate, dir);
18079 newDate = new Date(this.date);
18080 newDate.setUTCDate(this.date.getUTCDate() + dir);
18081 newViewDate = new Date(this.viewDate);
18082 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18084 if (this.dateWithinRange(newDate)){
18085 this.date = newDate;
18086 this.viewDate = newViewDate;
18087 this.setValue(this.formatDate(this.date));
18089 e.preventDefault();
18090 dateChanged = true;
18095 if (!this.keyboardNavigation) {
18098 dir = e.keyCode == 38 ? -1 : 1;
18100 newDate = this.moveYear(this.date, dir);
18101 newViewDate = this.moveYear(this.viewDate, dir);
18102 } else if (e.shiftKey){
18103 newDate = this.moveMonth(this.date, dir);
18104 newViewDate = this.moveMonth(this.viewDate, dir);
18106 newDate = new Date(this.date);
18107 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18108 newViewDate = new Date(this.viewDate);
18109 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18111 if (this.dateWithinRange(newDate)){
18112 this.date = newDate;
18113 this.viewDate = newViewDate;
18114 this.setValue(this.formatDate(this.date));
18116 e.preventDefault();
18117 dateChanged = true;
18121 this.setValue(this.formatDate(this.date));
18123 e.preventDefault();
18126 this.setValue(this.formatDate(this.date));
18140 onClick: function(e)
18142 e.stopPropagation();
18143 e.preventDefault();
18145 var target = e.getTarget();
18147 if(target.nodeName.toLowerCase() === 'i'){
18148 target = Roo.get(target).dom.parentNode;
18151 var nodeName = target.nodeName;
18152 var className = target.className;
18153 var html = target.innerHTML;
18154 //Roo.log(nodeName);
18156 switch(nodeName.toLowerCase()) {
18158 switch(className) {
18164 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18165 switch(this.viewMode){
18167 this.viewDate = this.moveMonth(this.viewDate, dir);
18171 this.viewDate = this.moveYear(this.viewDate, dir);
18177 var date = new Date();
18178 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18180 this.setValue(this.formatDate(this.date));
18187 if (className.indexOf('disabled') < 0) {
18188 this.viewDate.setUTCDate(1);
18189 if (className.indexOf('month') > -1) {
18190 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18192 var year = parseInt(html, 10) || 0;
18193 this.viewDate.setUTCFullYear(year);
18197 if(this.singleMode){
18198 this.setValue(this.formatDate(this.viewDate));
18209 //Roo.log(className);
18210 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18211 var day = parseInt(html, 10) || 1;
18212 var year = this.viewDate.getUTCFullYear(),
18213 month = this.viewDate.getUTCMonth();
18215 if (className.indexOf('old') > -1) {
18222 } else if (className.indexOf('new') > -1) {
18230 //Roo.log([year,month,day]);
18231 this.date = this.UTCDate(year, month, day,0,0,0,0);
18232 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18234 //Roo.log(this.formatDate(this.date));
18235 this.setValue(this.formatDate(this.date));
18242 setStartDate: function(startDate)
18244 this.startDate = startDate || -Infinity;
18245 if (this.startDate !== -Infinity) {
18246 this.startDate = this.parseDate(this.startDate);
18249 this.updateNavArrows();
18252 setEndDate: function(endDate)
18254 this.endDate = endDate || Infinity;
18255 if (this.endDate !== Infinity) {
18256 this.endDate = this.parseDate(this.endDate);
18259 this.updateNavArrows();
18262 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18264 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18265 if (typeof(this.daysOfWeekDisabled) !== 'object') {
18266 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18268 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18269 return parseInt(d, 10);
18272 this.updateNavArrows();
18275 updateNavArrows: function()
18277 if(this.singleMode){
18281 var d = new Date(this.viewDate),
18282 year = d.getUTCFullYear(),
18283 month = d.getUTCMonth();
18285 Roo.each(this.picker().select('.prev', true).elements, function(v){
18287 switch (this.viewMode) {
18290 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18296 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18303 Roo.each(this.picker().select('.next', true).elements, function(v){
18305 switch (this.viewMode) {
18308 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18314 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18322 moveMonth: function(date, dir)
18327 var new_date = new Date(date.valueOf()),
18328 day = new_date.getUTCDate(),
18329 month = new_date.getUTCMonth(),
18330 mag = Math.abs(dir),
18332 dir = dir > 0 ? 1 : -1;
18335 // If going back one month, make sure month is not current month
18336 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18338 return new_date.getUTCMonth() == month;
18340 // If going forward one month, make sure month is as expected
18341 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18343 return new_date.getUTCMonth() != new_month;
18345 new_month = month + dir;
18346 new_date.setUTCMonth(new_month);
18347 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18348 if (new_month < 0 || new_month > 11) {
18349 new_month = (new_month + 12) % 12;
18352 // For magnitudes >1, move one month at a time...
18353 for (var i=0; i<mag; i++) {
18354 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18355 new_date = this.moveMonth(new_date, dir);
18357 // ...then reset the day, keeping it in the new month
18358 new_month = new_date.getUTCMonth();
18359 new_date.setUTCDate(day);
18361 return new_month != new_date.getUTCMonth();
18364 // Common date-resetting loop -- if date is beyond end of month, make it
18367 new_date.setUTCDate(--day);
18368 new_date.setUTCMonth(new_month);
18373 moveYear: function(date, dir)
18375 return this.moveMonth(date, dir*12);
18378 dateWithinRange: function(date)
18380 return date >= this.startDate && date <= this.endDate;
18386 this.picker().remove();
18389 validateValue : function(value)
18391 if(value.length < 1) {
18392 if(this.allowBlank){
18398 if(value.length < this.minLength){
18401 if(value.length > this.maxLength){
18405 var vt = Roo.form.VTypes;
18406 if(!vt[this.vtype](value, this)){
18410 if(typeof this.validator == "function"){
18411 var msg = this.validator(value);
18417 if(this.regex && !this.regex.test(value)){
18421 if(typeof(this.parseDate(value)) == 'undefined'){
18425 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18429 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18439 Roo.apply(Roo.bootstrap.DateField, {
18450 html: '<i class="fa fa-arrow-left"/>'
18460 html: '<i class="fa fa-arrow-right"/>'
18502 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18503 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18504 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18505 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18506 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18519 navFnc: 'FullYear',
18524 navFnc: 'FullYear',
18529 Roo.apply(Roo.bootstrap.DateField, {
18533 cls: 'datepicker dropdown-menu roo-dynamic',
18537 cls: 'datepicker-days',
18541 cls: 'table-condensed',
18543 Roo.bootstrap.DateField.head,
18547 Roo.bootstrap.DateField.footer
18554 cls: 'datepicker-months',
18558 cls: 'table-condensed',
18560 Roo.bootstrap.DateField.head,
18561 Roo.bootstrap.DateField.content,
18562 Roo.bootstrap.DateField.footer
18569 cls: 'datepicker-years',
18573 cls: 'table-condensed',
18575 Roo.bootstrap.DateField.head,
18576 Roo.bootstrap.DateField.content,
18577 Roo.bootstrap.DateField.footer
18596 * @class Roo.bootstrap.TimeField
18597 * @extends Roo.bootstrap.Input
18598 * Bootstrap DateField class
18602 * Create a new TimeField
18603 * @param {Object} config The config object
18606 Roo.bootstrap.TimeField = function(config){
18607 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18611 * Fires when this field show.
18612 * @param {Roo.bootstrap.DateField} thisthis
18613 * @param {Mixed} date The date value
18618 * Fires when this field hide.
18619 * @param {Roo.bootstrap.DateField} this
18620 * @param {Mixed} date The date value
18625 * Fires when select a date.
18626 * @param {Roo.bootstrap.DateField} this
18627 * @param {Mixed} date The date value
18633 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18636 * @cfg {String} format
18637 * The default time format string which can be overriden for localization support. The format must be
18638 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18642 onRender: function(ct, position)
18645 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18647 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18649 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18651 this.pop = this.picker().select('>.datepicker-time',true).first();
18652 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18654 this.picker().on('mousedown', this.onMousedown, this);
18655 this.picker().on('click', this.onClick, this);
18657 this.picker().addClass('datepicker-dropdown');
18662 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18663 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18664 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18665 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18666 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18667 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18671 fireKey: function(e){
18672 if (!this.picker().isVisible()){
18673 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18679 e.preventDefault();
18687 this.onTogglePeriod();
18690 this.onIncrementMinutes();
18693 this.onDecrementMinutes();
18702 onClick: function(e) {
18703 e.stopPropagation();
18704 e.preventDefault();
18707 picker : function()
18709 return this.el.select('.datepicker', true).first();
18712 fillTime: function()
18714 var time = this.pop.select('tbody', true).first();
18716 time.dom.innerHTML = '';
18731 cls: 'hours-up glyphicon glyphicon-chevron-up'
18751 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18772 cls: 'timepicker-hour',
18787 cls: 'timepicker-minute',
18802 cls: 'btn btn-primary period',
18824 cls: 'hours-down glyphicon glyphicon-chevron-down'
18844 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18862 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18869 var hours = this.time.getHours();
18870 var minutes = this.time.getMinutes();
18883 hours = hours - 12;
18887 hours = '0' + hours;
18891 minutes = '0' + minutes;
18894 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18895 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18896 this.pop.select('button', true).first().dom.innerHTML = period;
18902 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18904 var cls = ['bottom'];
18906 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18913 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18918 this.picker().addClass(cls.join('-'));
18922 Roo.each(cls, function(c){
18924 _this.picker().setTop(_this.inputEl().getHeight());
18928 _this.picker().setTop(0 - _this.picker().getHeight());
18933 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18937 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18944 onFocus : function()
18946 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18950 onBlur : function()
18952 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18958 this.picker().show();
18963 this.fireEvent('show', this, this.date);
18968 this.picker().hide();
18971 this.fireEvent('hide', this, this.date);
18974 setTime : function()
18977 this.setValue(this.time.format(this.format));
18979 this.fireEvent('select', this, this.date);
18984 onMousedown: function(e){
18985 e.stopPropagation();
18986 e.preventDefault();
18989 onIncrementHours: function()
18991 Roo.log('onIncrementHours');
18992 this.time = this.time.add(Date.HOUR, 1);
18997 onDecrementHours: function()
18999 Roo.log('onDecrementHours');
19000 this.time = this.time.add(Date.HOUR, -1);
19004 onIncrementMinutes: function()
19006 Roo.log('onIncrementMinutes');
19007 this.time = this.time.add(Date.MINUTE, 1);
19011 onDecrementMinutes: function()
19013 Roo.log('onDecrementMinutes');
19014 this.time = this.time.add(Date.MINUTE, -1);
19018 onTogglePeriod: function()
19020 Roo.log('onTogglePeriod');
19021 this.time = this.time.add(Date.HOUR, 12);
19028 Roo.apply(Roo.bootstrap.TimeField, {
19058 cls: 'btn btn-info ok',
19070 Roo.apply(Roo.bootstrap.TimeField, {
19074 cls: 'datepicker dropdown-menu',
19078 cls: 'datepicker-time',
19082 cls: 'table-condensed',
19084 Roo.bootstrap.TimeField.content,
19085 Roo.bootstrap.TimeField.footer
19104 * @class Roo.bootstrap.MonthField
19105 * @extends Roo.bootstrap.Input
19106 * Bootstrap MonthField class
19108 * @cfg {String} language default en
19111 * Create a new MonthField
19112 * @param {Object} config The config object
19115 Roo.bootstrap.MonthField = function(config){
19116 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19121 * Fires when this field show.
19122 * @param {Roo.bootstrap.MonthField} this
19123 * @param {Mixed} date The date value
19128 * Fires when this field hide.
19129 * @param {Roo.bootstrap.MonthField} this
19130 * @param {Mixed} date The date value
19135 * Fires when select a date.
19136 * @param {Roo.bootstrap.MonthField} this
19137 * @param {String} oldvalue The old value
19138 * @param {String} newvalue The new value
19144 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
19146 onRender: function(ct, position)
19149 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19151 this.language = this.language || 'en';
19152 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19153 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19155 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19156 this.isInline = false;
19157 this.isInput = true;
19158 this.component = this.el.select('.add-on', true).first() || false;
19159 this.component = (this.component && this.component.length === 0) ? false : this.component;
19160 this.hasInput = this.component && this.inputEL().length;
19162 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19164 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19166 this.picker().on('mousedown', this.onMousedown, this);
19167 this.picker().on('click', this.onClick, this);
19169 this.picker().addClass('datepicker-dropdown');
19171 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19172 v.setStyle('width', '189px');
19179 if(this.isInline) {
19185 setValue: function(v, suppressEvent)
19187 var o = this.getValue();
19189 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19193 if(suppressEvent !== true){
19194 this.fireEvent('select', this, o, v);
19199 getValue: function()
19204 onClick: function(e)
19206 e.stopPropagation();
19207 e.preventDefault();
19209 var target = e.getTarget();
19211 if(target.nodeName.toLowerCase() === 'i'){
19212 target = Roo.get(target).dom.parentNode;
19215 var nodeName = target.nodeName;
19216 var className = target.className;
19217 var html = target.innerHTML;
19219 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19223 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19225 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19231 picker : function()
19233 return this.pickerEl;
19236 fillMonths: function()
19239 var months = this.picker().select('>.datepicker-months td', true).first();
19241 months.dom.innerHTML = '';
19247 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19250 months.createChild(month);
19259 if(typeof(this.vIndex) == 'undefined' && this.value.length){
19260 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19263 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19264 e.removeClass('active');
19266 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19267 e.addClass('active');
19274 if(this.isInline) {
19278 this.picker().removeClass(['bottom', 'top']);
19280 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19282 * place to the top of element!
19286 this.picker().addClass('top');
19287 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19292 this.picker().addClass('bottom');
19294 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19297 onFocus : function()
19299 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19303 onBlur : function()
19305 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19307 var d = this.inputEl().getValue();
19316 this.picker().show();
19317 this.picker().select('>.datepicker-months', true).first().show();
19321 this.fireEvent('show', this, this.date);
19326 if(this.isInline) {
19329 this.picker().hide();
19330 this.fireEvent('hide', this, this.date);
19334 onMousedown: function(e)
19336 e.stopPropagation();
19337 e.preventDefault();
19342 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19346 fireKey: function(e)
19348 if (!this.picker().isVisible()){
19349 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19360 e.preventDefault();
19364 dir = e.keyCode == 37 ? -1 : 1;
19366 this.vIndex = this.vIndex + dir;
19368 if(this.vIndex < 0){
19372 if(this.vIndex > 11){
19376 if(isNaN(this.vIndex)){
19380 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19386 dir = e.keyCode == 38 ? -1 : 1;
19388 this.vIndex = this.vIndex + dir * 4;
19390 if(this.vIndex < 0){
19394 if(this.vIndex > 11){
19398 if(isNaN(this.vIndex)){
19402 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19407 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19408 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19412 e.preventDefault();
19415 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19416 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19432 this.picker().remove();
19437 Roo.apply(Roo.bootstrap.MonthField, {
19456 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19457 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19462 Roo.apply(Roo.bootstrap.MonthField, {
19466 cls: 'datepicker dropdown-menu roo-dynamic',
19470 cls: 'datepicker-months',
19474 cls: 'table-condensed',
19476 Roo.bootstrap.DateField.content
19496 * @class Roo.bootstrap.CheckBox
19497 * @extends Roo.bootstrap.Input
19498 * Bootstrap CheckBox class
19500 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19501 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19502 * @cfg {String} boxLabel The text that appears beside the checkbox
19503 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19504 * @cfg {Boolean} checked initnal the element
19505 * @cfg {Boolean} inline inline the element (default false)
19506 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19509 * Create a new CheckBox
19510 * @param {Object} config The config object
19513 Roo.bootstrap.CheckBox = function(config){
19514 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19519 * Fires when the element is checked or unchecked.
19520 * @param {Roo.bootstrap.CheckBox} this This input
19521 * @param {Boolean} checked The new checked value
19528 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
19530 inputType: 'checkbox',
19538 getAutoCreate : function()
19540 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19546 cfg.cls = 'form-group ' + this.inputType; //input-group
19549 cfg.cls += ' ' + this.inputType + '-inline';
19555 type : this.inputType,
19556 value : this.inputValue,
19557 cls : 'roo-' + this.inputType, //'form-box',
19558 placeholder : this.placeholder || ''
19562 if(this.inputType != 'radio'){
19566 cls : 'roo-hidden-value',
19567 value : this.checked ? this.valueOff : this.inputValue
19572 if (this.weight) { // Validity check?
19573 cfg.cls += " " + this.inputType + "-" + this.weight;
19576 if (this.disabled) {
19577 input.disabled=true;
19581 input.checked = this.checked;
19588 input.name = this.name;
19590 if(this.inputType != 'radio'){
19591 hidden.name = this.name;
19592 input.name = '_hidden_' + this.name;
19597 input.cls += ' input-' + this.size;
19602 ['xs','sm','md','lg'].map(function(size){
19603 if (settings[size]) {
19604 cfg.cls += ' col-' + size + '-' + settings[size];
19608 var inputblock = input;
19610 if (this.before || this.after) {
19613 cls : 'input-group',
19618 inputblock.cn.push({
19620 cls : 'input-group-addon',
19625 inputblock.cn.push(input);
19627 if(this.inputType != 'radio'){
19628 inputblock.cn.push(hidden);
19632 inputblock.cn.push({
19634 cls : 'input-group-addon',
19641 if (align ==='left' && this.fieldLabel.length) {
19642 // Roo.log("left and has label");
19648 cls : 'control-label col-md-' + this.labelWidth,
19649 html : this.fieldLabel
19653 cls : "col-md-" + (12 - this.labelWidth),
19660 } else if ( this.fieldLabel.length) {
19661 // Roo.log(" label");
19665 tag: this.boxLabel ? 'span' : 'label',
19667 cls: 'control-label box-input-label',
19668 //cls : 'input-group-addon',
19669 html : this.fieldLabel
19679 // Roo.log(" no label && no align");
19680 cfg.cn = [ inputblock ] ;
19686 var boxLabelCfg = {
19688 //'for': id, // box label is handled by onclick - so no for...
19690 html: this.boxLabel
19694 boxLabelCfg.tooltip = this.tooltip;
19697 cfg.cn.push(boxLabelCfg);
19700 if(this.inputType != 'radio'){
19701 cfg.cn.push(hidden);
19709 * return the real input element.
19711 inputEl: function ()
19713 return this.el.select('input.roo-' + this.inputType,true).first();
19715 hiddenEl: function ()
19717 return this.el.select('input.roo-hidden-value',true).first();
19720 labelEl: function()
19722 return this.el.select('label.control-label',true).first();
19724 /* depricated... */
19728 return this.labelEl();
19731 boxLabelEl: function()
19733 return this.el.select('label.box-label',true).first();
19736 initEvents : function()
19738 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19740 this.inputEl().on('click', this.onClick, this);
19742 if (this.boxLabel) {
19743 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19746 this.startValue = this.getValue();
19749 Roo.bootstrap.CheckBox.register(this);
19753 onClick : function()
19755 this.setChecked(!this.checked);
19758 setChecked : function(state,suppressEvent)
19760 this.startValue = this.getValue();
19762 if(this.inputType == 'radio'){
19764 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19765 e.dom.checked = false;
19768 this.inputEl().dom.checked = true;
19770 this.inputEl().dom.value = this.inputValue;
19772 if(suppressEvent !== true){
19773 this.fireEvent('check', this, true);
19781 this.checked = state;
19783 this.inputEl().dom.checked = state;
19786 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19788 if(suppressEvent !== true){
19789 this.fireEvent('check', this, state);
19795 getValue : function()
19797 if(this.inputType == 'radio'){
19798 return this.getGroupValue();
19801 return this.hiddenEl().dom.value;
19805 getGroupValue : function()
19807 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19811 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19814 setValue : function(v,suppressEvent)
19816 if(this.inputType == 'radio'){
19817 this.setGroupValue(v, suppressEvent);
19821 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19826 setGroupValue : function(v, suppressEvent)
19828 this.startValue = this.getValue();
19830 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19831 e.dom.checked = false;
19833 if(e.dom.value == v){
19834 e.dom.checked = true;
19838 if(suppressEvent !== true){
19839 this.fireEvent('check', this, true);
19847 validate : function()
19851 (this.inputType == 'radio' && this.validateRadio()) ||
19852 (this.inputType == 'checkbox' && this.validateCheckbox())
19858 this.markInvalid();
19862 validateRadio : function()
19864 if(this.allowBlank){
19870 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19871 if(!e.dom.checked){
19883 validateCheckbox : function()
19886 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19889 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19897 for(var i in group){
19902 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19909 * Mark this field as valid
19911 markValid : function()
19913 if(this.allowBlank){
19919 this.fireEvent('valid', this);
19921 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19924 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19931 if(this.inputType == 'radio'){
19932 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19933 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19934 e.findParent('.form-group', false, true).addClass(_this.validClass);
19941 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19942 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19946 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19952 for(var i in group){
19953 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19954 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19959 * Mark this field as invalid
19960 * @param {String} msg The validation message
19962 markInvalid : function(msg)
19964 if(this.allowBlank){
19970 this.fireEvent('invalid', this, msg);
19972 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19975 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19979 label.markInvalid();
19982 if(this.inputType == 'radio'){
19983 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19984 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19985 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19992 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19993 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19997 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20003 for(var i in group){
20004 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20005 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20010 disable : function()
20012 if(this.inputType != 'radio'){
20013 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20020 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20021 _this.getActionEl().addClass(this.disabledClass);
20022 e.dom.disabled = true;
20026 this.disabled = true;
20027 this.fireEvent("disable", this);
20031 enable : function()
20033 if(this.inputType != 'radio'){
20034 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20041 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20042 _this.getActionEl().removeClass(this.disabledClass);
20043 e.dom.disabled = false;
20047 this.disabled = false;
20048 this.fireEvent("enable", this);
20054 Roo.apply(Roo.bootstrap.CheckBox, {
20059 * register a CheckBox Group
20060 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20062 register : function(checkbox)
20064 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20065 this.groups[checkbox.groupId] = {};
20068 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20072 this.groups[checkbox.groupId][checkbox.name] = checkbox;
20076 * fetch a CheckBox Group based on the group ID
20077 * @param {string} the group ID
20078 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20080 get: function(groupId) {
20081 if (typeof(this.groups[groupId]) == 'undefined') {
20085 return this.groups[groupId] ;
20097 *<div class="radio">
20099 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
20100 Option one is this and that—be sure to include why it's great
20107 *<label class="radio-inline">fieldLabel</label>
20108 *<label class="radio-inline">
20109 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
20117 * @class Roo.bootstrap.Radio
20118 * @extends Roo.bootstrap.CheckBox
20119 * Bootstrap Radio class
20122 * Create a new Radio
20123 * @param {Object} config The config object
20126 Roo.bootstrap.Radio = function(config){
20127 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20131 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
20133 inputType: 'radio',
20137 getAutoCreate : function()
20139 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20140 align = align || 'left'; // default...
20147 tag : this.inline ? 'span' : 'div',
20148 cls : 'form-group',
20152 var inline = this.inline ? ' radio-inline' : '';
20156 // does not need for, as we wrap the input with it..
20158 cls : 'control-label box-label' + inline,
20161 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
20165 //cls : 'control-label' + inline,
20166 html : this.fieldLabel,
20167 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
20173 type : this.inputType,
20174 //value : (!this.checked) ? this.valueOff : this.inputValue,
20175 value : this.inputValue,
20177 placeholder : this.placeholder || '' // ?? needed????
20180 if (this.weight) { // Validity check?
20181 input.cls += " radio-" + this.weight;
20183 if (this.disabled) {
20184 input.disabled=true;
20188 input.checked = this.checked;
20192 input.name = this.name;
20196 input.cls += ' input-' + this.size;
20199 //?? can span's inline have a width??
20202 ['xs','sm','md','lg'].map(function(size){
20203 if (settings[size]) {
20204 cfg.cls += ' col-' + size + '-' + settings[size];
20208 var inputblock = input;
20210 if (this.before || this.after) {
20213 cls : 'input-group',
20218 inputblock.cn.push({
20220 cls : 'input-group-addon',
20224 inputblock.cn.push(input);
20226 inputblock.cn.push({
20228 cls : 'input-group-addon',
20236 if (this.fieldLabel && this.fieldLabel.length) {
20237 cfg.cn.push(fieldLabel);
20240 // normal bootstrap puts the input inside the label.
20241 // however with our styled version - it has to go after the input.
20243 //lbl.cn.push(inputblock);
20247 cls: 'radio' + inline,
20254 cfg.cn.push( lblwrap);
20259 html: this.boxLabel
20268 initEvents : function()
20270 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20272 this.inputEl().on('click', this.onClick, this);
20273 if (this.boxLabel) {
20274 //Roo.log('find label');
20275 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
20280 inputEl: function ()
20282 return this.el.select('input.roo-radio',true).first();
20284 onClick : function()
20287 this.setChecked(true);
20290 setChecked : function(state,suppressEvent)
20293 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20294 v.dom.checked = false;
20297 Roo.log(this.inputEl().dom);
20298 this.checked = state;
20299 this.inputEl().dom.checked = state;
20301 if(suppressEvent !== true){
20302 this.fireEvent('check', this, state);
20305 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20309 getGroupValue : function()
20312 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20313 if(v.dom.checked == true){
20314 value = v.dom.value;
20322 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
20323 * @return {Mixed} value The field value
20325 getValue : function(){
20326 return this.getGroupValue();
20330 //<script type="text/javascript">
20333 * Based Ext JS Library 1.1.1
20334 * Copyright(c) 2006-2007, Ext JS, LLC.
20340 * @class Roo.HtmlEditorCore
20341 * @extends Roo.Component
20342 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20344 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20347 Roo.HtmlEditorCore = function(config){
20350 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20355 * @event initialize
20356 * Fires when the editor is fully initialized (including the iframe)
20357 * @param {Roo.HtmlEditorCore} this
20362 * Fires when the editor is first receives the focus. Any insertion must wait
20363 * until after this event.
20364 * @param {Roo.HtmlEditorCore} this
20368 * @event beforesync
20369 * Fires before the textarea is updated with content from the editor iframe. Return false
20370 * to cancel the sync.
20371 * @param {Roo.HtmlEditorCore} this
20372 * @param {String} html
20376 * @event beforepush
20377 * Fires before the iframe editor is updated with content from the textarea. Return false
20378 * to cancel the push.
20379 * @param {Roo.HtmlEditorCore} this
20380 * @param {String} html
20385 * Fires when the textarea is updated with content from the editor iframe.
20386 * @param {Roo.HtmlEditorCore} this
20387 * @param {String} html
20392 * Fires when the iframe editor is updated with content from the textarea.
20393 * @param {Roo.HtmlEditorCore} this
20394 * @param {String} html
20399 * @event editorevent
20400 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20401 * @param {Roo.HtmlEditorCore} this
20407 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20409 // defaults : white / black...
20410 this.applyBlacklists();
20417 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20421 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20427 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20432 * @cfg {Number} height (in pixels)
20436 * @cfg {Number} width (in pixels)
20441 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20444 stylesheets: false,
20449 // private properties
20450 validationEvent : false,
20452 initialized : false,
20454 sourceEditMode : false,
20455 onFocus : Roo.emptyFn,
20457 hideMode:'offsets',
20461 // blacklist + whitelisted elements..
20468 * Protected method that will not generally be called directly. It
20469 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20470 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20472 getDocMarkup : function(){
20476 // inherit styels from page...??
20477 if (this.stylesheets === false) {
20479 Roo.get(document.head).select('style').each(function(node) {
20480 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20483 Roo.get(document.head).select('link').each(function(node) {
20484 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20487 } else if (!this.stylesheets.length) {
20489 st = '<style type="text/css">' +
20490 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20496 st += '<style type="text/css">' +
20497 'IMG { cursor: pointer } ' +
20501 return '<html><head>' + st +
20502 //<style type="text/css">' +
20503 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20505 ' </head><body class="roo-htmleditor-body"></body></html>';
20509 onRender : function(ct, position)
20512 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20513 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20516 this.el.dom.style.border = '0 none';
20517 this.el.dom.setAttribute('tabIndex', -1);
20518 this.el.addClass('x-hidden hide');
20522 if(Roo.isIE){ // fix IE 1px bogus margin
20523 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20527 this.frameId = Roo.id();
20531 var iframe = this.owner.wrap.createChild({
20533 cls: 'form-control', // bootstrap..
20535 name: this.frameId,
20536 frameBorder : 'no',
20537 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20542 this.iframe = iframe.dom;
20544 this.assignDocWin();
20546 this.doc.designMode = 'on';
20549 this.doc.write(this.getDocMarkup());
20553 var task = { // must defer to wait for browser to be ready
20555 //console.log("run task?" + this.doc.readyState);
20556 this.assignDocWin();
20557 if(this.doc.body || this.doc.readyState == 'complete'){
20559 this.doc.designMode="on";
20563 Roo.TaskMgr.stop(task);
20564 this.initEditor.defer(10, this);
20571 Roo.TaskMgr.start(task);
20576 onResize : function(w, h)
20578 Roo.log('resize: ' +w + ',' + h );
20579 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20583 if(typeof w == 'number'){
20585 this.iframe.style.width = w + 'px';
20587 if(typeof h == 'number'){
20589 this.iframe.style.height = h + 'px';
20591 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20598 * Toggles the editor between standard and source edit mode.
20599 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20601 toggleSourceEdit : function(sourceEditMode){
20603 this.sourceEditMode = sourceEditMode === true;
20605 if(this.sourceEditMode){
20607 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20610 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20611 //this.iframe.className = '';
20614 //this.setSize(this.owner.wrap.getSize());
20615 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20622 * Protected method that will not generally be called directly. If you need/want
20623 * custom HTML cleanup, this is the method you should override.
20624 * @param {String} html The HTML to be cleaned
20625 * return {String} The cleaned HTML
20627 cleanHtml : function(html){
20628 html = String(html);
20629 if(html.length > 5){
20630 if(Roo.isSafari){ // strip safari nonsense
20631 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20634 if(html == ' '){
20641 * HTML Editor -> Textarea
20642 * Protected method that will not generally be called directly. Syncs the contents
20643 * of the editor iframe with the textarea.
20645 syncValue : function(){
20646 if(this.initialized){
20647 var bd = (this.doc.body || this.doc.documentElement);
20648 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20649 var html = bd.innerHTML;
20651 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20652 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20654 html = '<div style="'+m[0]+'">' + html + '</div>';
20657 html = this.cleanHtml(html);
20658 // fix up the special chars.. normaly like back quotes in word...
20659 // however we do not want to do this with chinese..
20660 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20661 var cc = b.charCodeAt();
20663 (cc >= 0x4E00 && cc < 0xA000 ) ||
20664 (cc >= 0x3400 && cc < 0x4E00 ) ||
20665 (cc >= 0xf900 && cc < 0xfb00 )
20671 if(this.owner.fireEvent('beforesync', this, html) !== false){
20672 this.el.dom.value = html;
20673 this.owner.fireEvent('sync', this, html);
20679 * Protected method that will not generally be called directly. Pushes the value of the textarea
20680 * into the iframe editor.
20682 pushValue : function(){
20683 if(this.initialized){
20684 var v = this.el.dom.value.trim();
20686 // if(v.length < 1){
20690 if(this.owner.fireEvent('beforepush', this, v) !== false){
20691 var d = (this.doc.body || this.doc.documentElement);
20693 this.cleanUpPaste();
20694 this.el.dom.value = d.innerHTML;
20695 this.owner.fireEvent('push', this, v);
20701 deferFocus : function(){
20702 this.focus.defer(10, this);
20706 focus : function(){
20707 if(this.win && !this.sourceEditMode){
20714 assignDocWin: function()
20716 var iframe = this.iframe;
20719 this.doc = iframe.contentWindow.document;
20720 this.win = iframe.contentWindow;
20722 // if (!Roo.get(this.frameId)) {
20725 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20726 // this.win = Roo.get(this.frameId).dom.contentWindow;
20728 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20732 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20733 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20738 initEditor : function(){
20739 //console.log("INIT EDITOR");
20740 this.assignDocWin();
20744 this.doc.designMode="on";
20746 this.doc.write(this.getDocMarkup());
20749 var dbody = (this.doc.body || this.doc.documentElement);
20750 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20751 // this copies styles from the containing element into thsi one..
20752 // not sure why we need all of this..
20753 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20755 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20756 //ss['background-attachment'] = 'fixed'; // w3c
20757 dbody.bgProperties = 'fixed'; // ie
20758 //Roo.DomHelper.applyStyles(dbody, ss);
20759 Roo.EventManager.on(this.doc, {
20760 //'mousedown': this.onEditorEvent,
20761 'mouseup': this.onEditorEvent,
20762 'dblclick': this.onEditorEvent,
20763 'click': this.onEditorEvent,
20764 'keyup': this.onEditorEvent,
20769 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20771 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20772 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20774 this.initialized = true;
20776 this.owner.fireEvent('initialize', this);
20781 onDestroy : function(){
20787 //for (var i =0; i < this.toolbars.length;i++) {
20788 // // fixme - ask toolbars for heights?
20789 // this.toolbars[i].onDestroy();
20792 //this.wrap.dom.innerHTML = '';
20793 //this.wrap.remove();
20798 onFirstFocus : function(){
20800 this.assignDocWin();
20803 this.activated = true;
20806 if(Roo.isGecko){ // prevent silly gecko errors
20808 var s = this.win.getSelection();
20809 if(!s.focusNode || s.focusNode.nodeType != 3){
20810 var r = s.getRangeAt(0);
20811 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20816 this.execCmd('useCSS', true);
20817 this.execCmd('styleWithCSS', false);
20820 this.owner.fireEvent('activate', this);
20824 adjustFont: function(btn){
20825 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20826 //if(Roo.isSafari){ // safari
20829 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20830 if(Roo.isSafari){ // safari
20831 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20832 v = (v < 10) ? 10 : v;
20833 v = (v > 48) ? 48 : v;
20834 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20839 v = Math.max(1, v+adjust);
20841 this.execCmd('FontSize', v );
20844 onEditorEvent : function(e)
20846 this.owner.fireEvent('editorevent', this, e);
20847 // this.updateToolbar();
20848 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20851 insertTag : function(tg)
20853 // could be a bit smarter... -> wrap the current selected tRoo..
20854 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20856 range = this.createRange(this.getSelection());
20857 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20858 wrappingNode.appendChild(range.extractContents());
20859 range.insertNode(wrappingNode);
20866 this.execCmd("formatblock", tg);
20870 insertText : function(txt)
20874 var range = this.createRange();
20875 range.deleteContents();
20876 //alert(Sender.getAttribute('label'));
20878 range.insertNode(this.doc.createTextNode(txt));
20884 * Executes a Midas editor command on the editor document and performs necessary focus and
20885 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20886 * @param {String} cmd The Midas command
20887 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20889 relayCmd : function(cmd, value){
20891 this.execCmd(cmd, value);
20892 this.owner.fireEvent('editorevent', this);
20893 //this.updateToolbar();
20894 this.owner.deferFocus();
20898 * Executes a Midas editor command directly on the editor document.
20899 * For visual commands, you should use {@link #relayCmd} instead.
20900 * <b>This should only be called after the editor is initialized.</b>
20901 * @param {String} cmd The Midas command
20902 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20904 execCmd : function(cmd, value){
20905 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20912 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20914 * @param {String} text | dom node..
20916 insertAtCursor : function(text)
20921 if(!this.activated){
20927 var r = this.doc.selection.createRange();
20938 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20942 // from jquery ui (MIT licenced)
20944 var win = this.win;
20946 if (win.getSelection && win.getSelection().getRangeAt) {
20947 range = win.getSelection().getRangeAt(0);
20948 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20949 range.insertNode(node);
20950 } else if (win.document.selection && win.document.selection.createRange) {
20951 // no firefox support
20952 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20953 win.document.selection.createRange().pasteHTML(txt);
20955 // no firefox support
20956 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20957 this.execCmd('InsertHTML', txt);
20966 mozKeyPress : function(e){
20968 var c = e.getCharCode(), cmd;
20971 c = String.fromCharCode(c).toLowerCase();
20985 this.cleanUpPaste.defer(100, this);
20993 e.preventDefault();
21001 fixKeys : function(){ // load time branching for fastest keydown performance
21003 return function(e){
21004 var k = e.getKey(), r;
21007 r = this.doc.selection.createRange();
21010 r.pasteHTML('    ');
21017 r = this.doc.selection.createRange();
21019 var target = r.parentElement();
21020 if(!target || target.tagName.toLowerCase() != 'li'){
21022 r.pasteHTML('<br />');
21028 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21029 this.cleanUpPaste.defer(100, this);
21035 }else if(Roo.isOpera){
21036 return function(e){
21037 var k = e.getKey();
21041 this.execCmd('InsertHTML','    ');
21044 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21045 this.cleanUpPaste.defer(100, this);
21050 }else if(Roo.isSafari){
21051 return function(e){
21052 var k = e.getKey();
21056 this.execCmd('InsertText','\t');
21060 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21061 this.cleanUpPaste.defer(100, this);
21069 getAllAncestors: function()
21071 var p = this.getSelectedNode();
21074 a.push(p); // push blank onto stack..
21075 p = this.getParentElement();
21079 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21083 a.push(this.doc.body);
21087 lastSelNode : false,
21090 getSelection : function()
21092 this.assignDocWin();
21093 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21096 getSelectedNode: function()
21098 // this may only work on Gecko!!!
21100 // should we cache this!!!!
21105 var range = this.createRange(this.getSelection()).cloneRange();
21108 var parent = range.parentElement();
21110 var testRange = range.duplicate();
21111 testRange.moveToElementText(parent);
21112 if (testRange.inRange(range)) {
21115 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21118 parent = parent.parentElement;
21123 // is ancestor a text element.
21124 var ac = range.commonAncestorContainer;
21125 if (ac.nodeType == 3) {
21126 ac = ac.parentNode;
21129 var ar = ac.childNodes;
21132 var other_nodes = [];
21133 var has_other_nodes = false;
21134 for (var i=0;i<ar.length;i++) {
21135 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21138 // fullly contained node.
21140 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21145 // probably selected..
21146 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21147 other_nodes.push(ar[i]);
21151 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21156 has_other_nodes = true;
21158 if (!nodes.length && other_nodes.length) {
21159 nodes= other_nodes;
21161 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21167 createRange: function(sel)
21169 // this has strange effects when using with
21170 // top toolbar - not sure if it's a great idea.
21171 //this.editor.contentWindow.focus();
21172 if (typeof sel != "undefined") {
21174 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21176 return this.doc.createRange();
21179 return this.doc.createRange();
21182 getParentElement: function()
21185 this.assignDocWin();
21186 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21188 var range = this.createRange(sel);
21191 var p = range.commonAncestorContainer;
21192 while (p.nodeType == 3) { // text node
21203 * Range intersection.. the hard stuff...
21207 * [ -- selected range --- ]
21211 * if end is before start or hits it. fail.
21212 * if start is after end or hits it fail.
21214 * if either hits (but other is outside. - then it's not
21220 // @see http://www.thismuchiknow.co.uk/?p=64.
21221 rangeIntersectsNode : function(range, node)
21223 var nodeRange = node.ownerDocument.createRange();
21225 nodeRange.selectNode(node);
21227 nodeRange.selectNodeContents(node);
21230 var rangeStartRange = range.cloneRange();
21231 rangeStartRange.collapse(true);
21233 var rangeEndRange = range.cloneRange();
21234 rangeEndRange.collapse(false);
21236 var nodeStartRange = nodeRange.cloneRange();
21237 nodeStartRange.collapse(true);
21239 var nodeEndRange = nodeRange.cloneRange();
21240 nodeEndRange.collapse(false);
21242 return rangeStartRange.compareBoundaryPoints(
21243 Range.START_TO_START, nodeEndRange) == -1 &&
21244 rangeEndRange.compareBoundaryPoints(
21245 Range.START_TO_START, nodeStartRange) == 1;
21249 rangeCompareNode : function(range, node)
21251 var nodeRange = node.ownerDocument.createRange();
21253 nodeRange.selectNode(node);
21255 nodeRange.selectNodeContents(node);
21259 range.collapse(true);
21261 nodeRange.collapse(true);
21263 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21264 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
21266 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21268 var nodeIsBefore = ss == 1;
21269 var nodeIsAfter = ee == -1;
21271 if (nodeIsBefore && nodeIsAfter) {
21274 if (!nodeIsBefore && nodeIsAfter) {
21275 return 1; //right trailed.
21278 if (nodeIsBefore && !nodeIsAfter) {
21279 return 2; // left trailed.
21285 // private? - in a new class?
21286 cleanUpPaste : function()
21288 // cleans up the whole document..
21289 Roo.log('cleanuppaste');
21291 this.cleanUpChildren(this.doc.body);
21292 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21293 if (clean != this.doc.body.innerHTML) {
21294 this.doc.body.innerHTML = clean;
21299 cleanWordChars : function(input) {// change the chars to hex code
21300 var he = Roo.HtmlEditorCore;
21302 var output = input;
21303 Roo.each(he.swapCodes, function(sw) {
21304 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21306 output = output.replace(swapper, sw[1]);
21313 cleanUpChildren : function (n)
21315 if (!n.childNodes.length) {
21318 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21319 this.cleanUpChild(n.childNodes[i]);
21326 cleanUpChild : function (node)
21329 //console.log(node);
21330 if (node.nodeName == "#text") {
21331 // clean up silly Windows -- stuff?
21334 if (node.nodeName == "#comment") {
21335 node.parentNode.removeChild(node);
21336 // clean up silly Windows -- stuff?
21339 var lcname = node.tagName.toLowerCase();
21340 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21341 // whitelist of tags..
21343 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21345 node.parentNode.removeChild(node);
21350 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21352 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21353 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21355 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21356 // remove_keep_children = true;
21359 if (remove_keep_children) {
21360 this.cleanUpChildren(node);
21361 // inserts everything just before this node...
21362 while (node.childNodes.length) {
21363 var cn = node.childNodes[0];
21364 node.removeChild(cn);
21365 node.parentNode.insertBefore(cn, node);
21367 node.parentNode.removeChild(node);
21371 if (!node.attributes || !node.attributes.length) {
21372 this.cleanUpChildren(node);
21376 function cleanAttr(n,v)
21379 if (v.match(/^\./) || v.match(/^\//)) {
21382 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21385 if (v.match(/^#/)) {
21388 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21389 node.removeAttribute(n);
21393 var cwhite = this.cwhite;
21394 var cblack = this.cblack;
21396 function cleanStyle(n,v)
21398 if (v.match(/expression/)) { //XSS?? should we even bother..
21399 node.removeAttribute(n);
21403 var parts = v.split(/;/);
21406 Roo.each(parts, function(p) {
21407 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21411 var l = p.split(':').shift().replace(/\s+/g,'');
21412 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21414 if ( cwhite.length && cblack.indexOf(l) > -1) {
21415 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21416 //node.removeAttribute(n);
21420 // only allow 'c whitelisted system attributes'
21421 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21422 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21423 //node.removeAttribute(n);
21433 if (clean.length) {
21434 node.setAttribute(n, clean.join(';'));
21436 node.removeAttribute(n);
21442 for (var i = node.attributes.length-1; i > -1 ; i--) {
21443 var a = node.attributes[i];
21446 if (a.name.toLowerCase().substr(0,2)=='on') {
21447 node.removeAttribute(a.name);
21450 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21451 node.removeAttribute(a.name);
21454 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21455 cleanAttr(a.name,a.value); // fixme..
21458 if (a.name == 'style') {
21459 cleanStyle(a.name,a.value);
21462 /// clean up MS crap..
21463 // tecnically this should be a list of valid class'es..
21466 if (a.name == 'class') {
21467 if (a.value.match(/^Mso/)) {
21468 node.className = '';
21471 if (a.value.match(/body/)) {
21472 node.className = '';
21483 this.cleanUpChildren(node);
21489 * Clean up MS wordisms...
21491 cleanWord : function(node)
21496 this.cleanWord(this.doc.body);
21499 if (node.nodeName == "#text") {
21500 // clean up silly Windows -- stuff?
21503 if (node.nodeName == "#comment") {
21504 node.parentNode.removeChild(node);
21505 // clean up silly Windows -- stuff?
21509 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21510 node.parentNode.removeChild(node);
21514 // remove - but keep children..
21515 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21516 while (node.childNodes.length) {
21517 var cn = node.childNodes[0];
21518 node.removeChild(cn);
21519 node.parentNode.insertBefore(cn, node);
21521 node.parentNode.removeChild(node);
21522 this.iterateChildren(node, this.cleanWord);
21526 if (node.className.length) {
21528 var cn = node.className.split(/\W+/);
21530 Roo.each(cn, function(cls) {
21531 if (cls.match(/Mso[a-zA-Z]+/)) {
21536 node.className = cna.length ? cna.join(' ') : '';
21538 node.removeAttribute("class");
21542 if (node.hasAttribute("lang")) {
21543 node.removeAttribute("lang");
21546 if (node.hasAttribute("style")) {
21548 var styles = node.getAttribute("style").split(";");
21550 Roo.each(styles, function(s) {
21551 if (!s.match(/:/)) {
21554 var kv = s.split(":");
21555 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21558 // what ever is left... we allow.
21561 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21562 if (!nstyle.length) {
21563 node.removeAttribute('style');
21566 this.iterateChildren(node, this.cleanWord);
21572 * iterateChildren of a Node, calling fn each time, using this as the scole..
21573 * @param {DomNode} node node to iterate children of.
21574 * @param {Function} fn method of this class to call on each item.
21576 iterateChildren : function(node, fn)
21578 if (!node.childNodes.length) {
21581 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21582 fn.call(this, node.childNodes[i])
21588 * cleanTableWidths.
21590 * Quite often pasting from word etc.. results in tables with column and widths.
21591 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21594 cleanTableWidths : function(node)
21599 this.cleanTableWidths(this.doc.body);
21604 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21607 Roo.log(node.tagName);
21608 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21609 this.iterateChildren(node, this.cleanTableWidths);
21612 if (node.hasAttribute('width')) {
21613 node.removeAttribute('width');
21617 if (node.hasAttribute("style")) {
21620 var styles = node.getAttribute("style").split(";");
21622 Roo.each(styles, function(s) {
21623 if (!s.match(/:/)) {
21626 var kv = s.split(":");
21627 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21630 // what ever is left... we allow.
21633 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21634 if (!nstyle.length) {
21635 node.removeAttribute('style');
21639 this.iterateChildren(node, this.cleanTableWidths);
21647 domToHTML : function(currentElement, depth, nopadtext) {
21649 depth = depth || 0;
21650 nopadtext = nopadtext || false;
21652 if (!currentElement) {
21653 return this.domToHTML(this.doc.body);
21656 //Roo.log(currentElement);
21658 var allText = false;
21659 var nodeName = currentElement.nodeName;
21660 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21662 if (nodeName == '#text') {
21664 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21669 if (nodeName != 'BODY') {
21672 // Prints the node tagName, such as <A>, <IMG>, etc
21675 for(i = 0; i < currentElement.attributes.length;i++) {
21677 var aname = currentElement.attributes.item(i).name;
21678 if (!currentElement.attributes.item(i).value.length) {
21681 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21684 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21693 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21696 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21701 // Traverse the tree
21703 var currentElementChild = currentElement.childNodes.item(i);
21704 var allText = true;
21705 var innerHTML = '';
21707 while (currentElementChild) {
21708 // Formatting code (indent the tree so it looks nice on the screen)
21709 var nopad = nopadtext;
21710 if (lastnode == 'SPAN') {
21714 if (currentElementChild.nodeName == '#text') {
21715 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21716 toadd = nopadtext ? toadd : toadd.trim();
21717 if (!nopad && toadd.length > 80) {
21718 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21720 innerHTML += toadd;
21723 currentElementChild = currentElement.childNodes.item(i);
21729 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21731 // Recursively traverse the tree structure of the child node
21732 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21733 lastnode = currentElementChild.nodeName;
21735 currentElementChild=currentElement.childNodes.item(i);
21741 // The remaining code is mostly for formatting the tree
21742 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21747 ret+= "</"+tagName+">";
21753 applyBlacklists : function()
21755 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21756 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21760 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21761 if (b.indexOf(tag) > -1) {
21764 this.white.push(tag);
21768 Roo.each(w, function(tag) {
21769 if (b.indexOf(tag) > -1) {
21772 if (this.white.indexOf(tag) > -1) {
21775 this.white.push(tag);
21780 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21781 if (w.indexOf(tag) > -1) {
21784 this.black.push(tag);
21788 Roo.each(b, function(tag) {
21789 if (w.indexOf(tag) > -1) {
21792 if (this.black.indexOf(tag) > -1) {
21795 this.black.push(tag);
21800 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21801 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21805 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21806 if (b.indexOf(tag) > -1) {
21809 this.cwhite.push(tag);
21813 Roo.each(w, function(tag) {
21814 if (b.indexOf(tag) > -1) {
21817 if (this.cwhite.indexOf(tag) > -1) {
21820 this.cwhite.push(tag);
21825 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21826 if (w.indexOf(tag) > -1) {
21829 this.cblack.push(tag);
21833 Roo.each(b, function(tag) {
21834 if (w.indexOf(tag) > -1) {
21837 if (this.cblack.indexOf(tag) > -1) {
21840 this.cblack.push(tag);
21845 setStylesheets : function(stylesheets)
21847 if(typeof(stylesheets) == 'string'){
21848 Roo.get(this.iframe.contentDocument.head).createChild({
21850 rel : 'stylesheet',
21859 Roo.each(stylesheets, function(s) {
21864 Roo.get(_this.iframe.contentDocument.head).createChild({
21866 rel : 'stylesheet',
21875 removeStylesheets : function()
21879 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21884 // hide stuff that is not compatible
21898 * @event specialkey
21902 * @cfg {String} fieldClass @hide
21905 * @cfg {String} focusClass @hide
21908 * @cfg {String} autoCreate @hide
21911 * @cfg {String} inputType @hide
21914 * @cfg {String} invalidClass @hide
21917 * @cfg {String} invalidText @hide
21920 * @cfg {String} msgFx @hide
21923 * @cfg {String} validateOnBlur @hide
21927 Roo.HtmlEditorCore.white = [
21928 'area', 'br', 'img', 'input', 'hr', 'wbr',
21930 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21931 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21932 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21933 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21934 'table', 'ul', 'xmp',
21936 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21939 'dir', 'menu', 'ol', 'ul', 'dl',
21945 Roo.HtmlEditorCore.black = [
21946 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21948 'base', 'basefont', 'bgsound', 'blink', 'body',
21949 'frame', 'frameset', 'head', 'html', 'ilayer',
21950 'iframe', 'layer', 'link', 'meta', 'object',
21951 'script', 'style' ,'title', 'xml' // clean later..
21953 Roo.HtmlEditorCore.clean = [
21954 'script', 'style', 'title', 'xml'
21956 Roo.HtmlEditorCore.remove = [
21961 Roo.HtmlEditorCore.ablack = [
21965 Roo.HtmlEditorCore.aclean = [
21966 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21970 Roo.HtmlEditorCore.pwhite= [
21971 'http', 'https', 'mailto'
21974 // white listed style attributes.
21975 Roo.HtmlEditorCore.cwhite= [
21976 // 'text-align', /// default is to allow most things..
21982 // black listed style attributes.
21983 Roo.HtmlEditorCore.cblack= [
21984 // 'font-size' -- this can be set by the project
21988 Roo.HtmlEditorCore.swapCodes =[
22007 * @class Roo.bootstrap.HtmlEditor
22008 * @extends Roo.bootstrap.TextArea
22009 * Bootstrap HtmlEditor class
22012 * Create a new HtmlEditor
22013 * @param {Object} config The config object
22016 Roo.bootstrap.HtmlEditor = function(config){
22017 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22018 if (!this.toolbars) {
22019 this.toolbars = [];
22021 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22024 * @event initialize
22025 * Fires when the editor is fully initialized (including the iframe)
22026 * @param {HtmlEditor} this
22031 * Fires when the editor is first receives the focus. Any insertion must wait
22032 * until after this event.
22033 * @param {HtmlEditor} this
22037 * @event beforesync
22038 * Fires before the textarea is updated with content from the editor iframe. Return false
22039 * to cancel the sync.
22040 * @param {HtmlEditor} this
22041 * @param {String} html
22045 * @event beforepush
22046 * Fires before the iframe editor is updated with content from the textarea. Return false
22047 * to cancel the push.
22048 * @param {HtmlEditor} this
22049 * @param {String} html
22054 * Fires when the textarea is updated with content from the editor iframe.
22055 * @param {HtmlEditor} this
22056 * @param {String} html
22061 * Fires when the iframe editor is updated with content from the textarea.
22062 * @param {HtmlEditor} this
22063 * @param {String} html
22067 * @event editmodechange
22068 * Fires when the editor switches edit modes
22069 * @param {HtmlEditor} this
22070 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22072 editmodechange: true,
22074 * @event editorevent
22075 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22076 * @param {HtmlEditor} this
22080 * @event firstfocus
22081 * Fires when on first focus - needed by toolbars..
22082 * @param {HtmlEditor} this
22087 * Auto save the htmlEditor value as a file into Events
22088 * @param {HtmlEditor} this
22092 * @event savedpreview
22093 * preview the saved version of htmlEditor
22094 * @param {HtmlEditor} this
22101 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
22105 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22110 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22115 * @cfg {Number} height (in pixels)
22119 * @cfg {Number} width (in pixels)
22124 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22127 stylesheets: false,
22132 // private properties
22133 validationEvent : false,
22135 initialized : false,
22138 onFocus : Roo.emptyFn,
22140 hideMode:'offsets',
22143 tbContainer : false,
22145 toolbarContainer :function() {
22146 return this.wrap.select('.x-html-editor-tb',true).first();
22150 * Protected method that will not generally be called directly. It
22151 * is called when the editor creates its toolbar. Override this method if you need to
22152 * add custom toolbar buttons.
22153 * @param {HtmlEditor} editor
22155 createToolbar : function(){
22157 Roo.log("create toolbars");
22159 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22160 this.toolbars[0].render(this.toolbarContainer());
22164 // if (!editor.toolbars || !editor.toolbars.length) {
22165 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22168 // for (var i =0 ; i < editor.toolbars.length;i++) {
22169 // editor.toolbars[i] = Roo.factory(
22170 // typeof(editor.toolbars[i]) == 'string' ?
22171 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
22172 // Roo.bootstrap.HtmlEditor);
22173 // editor.toolbars[i].init(editor);
22179 onRender : function(ct, position)
22181 // Roo.log("Call onRender: " + this.xtype);
22183 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22185 this.wrap = this.inputEl().wrap({
22186 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22189 this.editorcore.onRender(ct, position);
22191 if (this.resizable) {
22192 this.resizeEl = new Roo.Resizable(this.wrap, {
22196 minHeight : this.height,
22197 height: this.height,
22198 handles : this.resizable,
22201 resize : function(r, w, h) {
22202 _t.onResize(w,h); // -something
22208 this.createToolbar(this);
22211 if(!this.width && this.resizable){
22212 this.setSize(this.wrap.getSize());
22214 if (this.resizeEl) {
22215 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22216 // should trigger onReize..
22222 onResize : function(w, h)
22224 Roo.log('resize: ' +w + ',' + h );
22225 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22229 if(this.inputEl() ){
22230 if(typeof w == 'number'){
22231 var aw = w - this.wrap.getFrameWidth('lr');
22232 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22235 if(typeof h == 'number'){
22236 var tbh = -11; // fixme it needs to tool bar size!
22237 for (var i =0; i < this.toolbars.length;i++) {
22238 // fixme - ask toolbars for heights?
22239 tbh += this.toolbars[i].el.getHeight();
22240 //if (this.toolbars[i].footer) {
22241 // tbh += this.toolbars[i].footer.el.getHeight();
22249 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22250 ah -= 5; // knock a few pixes off for look..
22251 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22255 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22256 this.editorcore.onResize(ew,eh);
22261 * Toggles the editor between standard and source edit mode.
22262 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22264 toggleSourceEdit : function(sourceEditMode)
22266 this.editorcore.toggleSourceEdit(sourceEditMode);
22268 if(this.editorcore.sourceEditMode){
22269 Roo.log('editor - showing textarea');
22272 // Roo.log(this.syncValue());
22274 this.inputEl().removeClass(['hide', 'x-hidden']);
22275 this.inputEl().dom.removeAttribute('tabIndex');
22276 this.inputEl().focus();
22278 Roo.log('editor - hiding textarea');
22280 // Roo.log(this.pushValue());
22283 this.inputEl().addClass(['hide', 'x-hidden']);
22284 this.inputEl().dom.setAttribute('tabIndex', -1);
22285 //this.deferFocus();
22288 if(this.resizable){
22289 this.setSize(this.wrap.getSize());
22292 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22295 // private (for BoxComponent)
22296 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22298 // private (for BoxComponent)
22299 getResizeEl : function(){
22303 // private (for BoxComponent)
22304 getPositionEl : function(){
22309 initEvents : function(){
22310 this.originalValue = this.getValue();
22314 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22317 // markInvalid : Roo.emptyFn,
22319 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22322 // clearInvalid : Roo.emptyFn,
22324 setValue : function(v){
22325 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22326 this.editorcore.pushValue();
22331 deferFocus : function(){
22332 this.focus.defer(10, this);
22336 focus : function(){
22337 this.editorcore.focus();
22343 onDestroy : function(){
22349 for (var i =0; i < this.toolbars.length;i++) {
22350 // fixme - ask toolbars for heights?
22351 this.toolbars[i].onDestroy();
22354 this.wrap.dom.innerHTML = '';
22355 this.wrap.remove();
22360 onFirstFocus : function(){
22361 //Roo.log("onFirstFocus");
22362 this.editorcore.onFirstFocus();
22363 for (var i =0; i < this.toolbars.length;i++) {
22364 this.toolbars[i].onFirstFocus();
22370 syncValue : function()
22372 this.editorcore.syncValue();
22375 pushValue : function()
22377 this.editorcore.pushValue();
22381 // hide stuff that is not compatible
22395 * @event specialkey
22399 * @cfg {String} fieldClass @hide
22402 * @cfg {String} focusClass @hide
22405 * @cfg {String} autoCreate @hide
22408 * @cfg {String} inputType @hide
22411 * @cfg {String} invalidClass @hide
22414 * @cfg {String} invalidText @hide
22417 * @cfg {String} msgFx @hide
22420 * @cfg {String} validateOnBlur @hide
22429 Roo.namespace('Roo.bootstrap.htmleditor');
22431 * @class Roo.bootstrap.HtmlEditorToolbar1
22436 new Roo.bootstrap.HtmlEditor({
22439 new Roo.bootstrap.HtmlEditorToolbar1({
22440 disable : { fonts: 1 , format: 1, ..., ... , ...],
22446 * @cfg {Object} disable List of elements to disable..
22447 * @cfg {Array} btns List of additional buttons.
22451 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22454 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22457 Roo.apply(this, config);
22459 // default disabled, based on 'good practice'..
22460 this.disable = this.disable || {};
22461 Roo.applyIf(this.disable, {
22464 specialElements : true
22466 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22468 this.editor = config.editor;
22469 this.editorcore = config.editor.editorcore;
22471 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22473 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22474 // dont call parent... till later.
22476 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
22481 editorcore : false,
22486 "h1","h2","h3","h4","h5","h6",
22488 "abbr", "acronym", "address", "cite", "samp", "var",
22492 onRender : function(ct, position)
22494 // Roo.log("Call onRender: " + this.xtype);
22496 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22498 this.el.dom.style.marginBottom = '0';
22500 var editorcore = this.editorcore;
22501 var editor= this.editor;
22504 var btn = function(id,cmd , toggle, handler){
22506 var event = toggle ? 'toggle' : 'click';
22511 xns: Roo.bootstrap,
22514 enableToggle:toggle !== false,
22516 pressed : toggle ? false : null,
22519 a.listeners[toggle ? 'toggle' : 'click'] = function() {
22520 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
22529 xns: Roo.bootstrap,
22530 glyphicon : 'font',
22534 xns: Roo.bootstrap,
22538 Roo.each(this.formats, function(f) {
22539 style.menu.items.push({
22541 xns: Roo.bootstrap,
22542 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22547 editorcore.insertTag(this.tagname);
22554 children.push(style);
22557 btn('bold',false,true);
22558 btn('italic',false,true);
22559 btn('align-left', 'justifyleft',true);
22560 btn('align-center', 'justifycenter',true);
22561 btn('align-right' , 'justifyright',true);
22562 btn('link', false, false, function(btn) {
22563 //Roo.log("create link?");
22564 var url = prompt(this.createLinkText, this.defaultLinkValue);
22565 if(url && url != 'http:/'+'/'){
22566 this.editorcore.relayCmd('createlink', url);
22569 btn('list','insertunorderedlist',true);
22570 btn('pencil', false,true, function(btn){
22573 this.toggleSourceEdit(btn.pressed);
22579 xns: Roo.bootstrap,
22584 xns: Roo.bootstrap,
22589 cog.menu.items.push({
22591 xns: Roo.bootstrap,
22592 html : Clean styles,
22597 editorcore.insertTag(this.tagname);
22606 this.xtype = 'NavSimplebar';
22608 for(var i=0;i< children.length;i++) {
22610 this.buttons.add(this.addxtypeChild(children[i]));
22614 editor.on('editorevent', this.updateToolbar, this);
22616 onBtnClick : function(id)
22618 this.editorcore.relayCmd(id);
22619 this.editorcore.focus();
22623 * Protected method that will not generally be called directly. It triggers
22624 * a toolbar update by reading the markup state of the current selection in the editor.
22626 updateToolbar: function(){
22628 if(!this.editorcore.activated){
22629 this.editor.onFirstFocus(); // is this neeed?
22633 var btns = this.buttons;
22634 var doc = this.editorcore.doc;
22635 btns.get('bold').setActive(doc.queryCommandState('bold'));
22636 btns.get('italic').setActive(doc.queryCommandState('italic'));
22637 //btns.get('underline').setActive(doc.queryCommandState('underline'));
22639 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22640 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22641 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22643 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22644 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22647 var ans = this.editorcore.getAllAncestors();
22648 if (this.formatCombo) {
22651 var store = this.formatCombo.store;
22652 this.formatCombo.setValue("");
22653 for (var i =0; i < ans.length;i++) {
22654 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22656 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22664 // hides menus... - so this cant be on a menu...
22665 Roo.bootstrap.MenuMgr.hideAll();
22667 Roo.bootstrap.MenuMgr.hideAll();
22668 //this.editorsyncValue();
22670 onFirstFocus: function() {
22671 this.buttons.each(function(item){
22675 toggleSourceEdit : function(sourceEditMode){
22678 if(sourceEditMode){
22679 Roo.log("disabling buttons");
22680 this.buttons.each( function(item){
22681 if(item.cmd != 'pencil'){
22687 Roo.log("enabling buttons");
22688 if(this.editorcore.initialized){
22689 this.buttons.each( function(item){
22695 Roo.log("calling toggole on editor");
22696 // tell the editor that it's been pressed..
22697 this.editor.toggleSourceEdit(sourceEditMode);
22707 * @class Roo.bootstrap.Table.AbstractSelectionModel
22708 * @extends Roo.util.Observable
22709 * Abstract base class for grid SelectionModels. It provides the interface that should be
22710 * implemented by descendant classes. This class should not be directly instantiated.
22713 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22714 this.locked = false;
22715 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22719 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22720 /** @ignore Called by the grid automatically. Do not call directly. */
22721 init : function(grid){
22727 * Locks the selections.
22730 this.locked = true;
22734 * Unlocks the selections.
22736 unlock : function(){
22737 this.locked = false;
22741 * Returns true if the selections are locked.
22742 * @return {Boolean}
22744 isLocked : function(){
22745 return this.locked;
22749 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22750 * @class Roo.bootstrap.Table.RowSelectionModel
22751 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22752 * It supports multiple selections and keyboard selection/navigation.
22754 * @param {Object} config
22757 Roo.bootstrap.Table.RowSelectionModel = function(config){
22758 Roo.apply(this, config);
22759 this.selections = new Roo.util.MixedCollection(false, function(o){
22764 this.lastActive = false;
22768 * @event selectionchange
22769 * Fires when the selection changes
22770 * @param {SelectionModel} this
22772 "selectionchange" : true,
22774 * @event afterselectionchange
22775 * Fires after the selection changes (eg. by key press or clicking)
22776 * @param {SelectionModel} this
22778 "afterselectionchange" : true,
22780 * @event beforerowselect
22781 * Fires when a row is selected being selected, return false to cancel.
22782 * @param {SelectionModel} this
22783 * @param {Number} rowIndex The selected index
22784 * @param {Boolean} keepExisting False if other selections will be cleared
22786 "beforerowselect" : true,
22789 * Fires when a row is selected.
22790 * @param {SelectionModel} this
22791 * @param {Number} rowIndex The selected index
22792 * @param {Roo.data.Record} r The record
22794 "rowselect" : true,
22796 * @event rowdeselect
22797 * Fires when a row is deselected.
22798 * @param {SelectionModel} this
22799 * @param {Number} rowIndex The selected index
22801 "rowdeselect" : true
22803 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22804 this.locked = false;
22807 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22809 * @cfg {Boolean} singleSelect
22810 * True to allow selection of only one row at a time (defaults to false)
22812 singleSelect : false,
22815 initEvents : function()
22818 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22819 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
22820 //}else{ // allow click to work like normal
22821 // this.grid.on("rowclick", this.handleDragableRowClick, this);
22823 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22824 this.grid.on("rowclick", this.handleMouseDown, this);
22826 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22827 "up" : function(e){
22829 this.selectPrevious(e.shiftKey);
22830 }else if(this.last !== false && this.lastActive !== false){
22831 var last = this.last;
22832 this.selectRange(this.last, this.lastActive-1);
22833 this.grid.getView().focusRow(this.lastActive);
22834 if(last !== false){
22838 this.selectFirstRow();
22840 this.fireEvent("afterselectionchange", this);
22842 "down" : function(e){
22844 this.selectNext(e.shiftKey);
22845 }else if(this.last !== false && this.lastActive !== false){
22846 var last = this.last;
22847 this.selectRange(this.last, this.lastActive+1);
22848 this.grid.getView().focusRow(this.lastActive);
22849 if(last !== false){
22853 this.selectFirstRow();
22855 this.fireEvent("afterselectionchange", this);
22859 this.grid.store.on('load', function(){
22860 this.selections.clear();
22863 var view = this.grid.view;
22864 view.on("refresh", this.onRefresh, this);
22865 view.on("rowupdated", this.onRowUpdated, this);
22866 view.on("rowremoved", this.onRemove, this);
22871 onRefresh : function()
22873 var ds = this.grid.store, i, v = this.grid.view;
22874 var s = this.selections;
22875 s.each(function(r){
22876 if((i = ds.indexOfId(r.id)) != -1){
22885 onRemove : function(v, index, r){
22886 this.selections.remove(r);
22890 onRowUpdated : function(v, index, r){
22891 if(this.isSelected(r)){
22892 v.onRowSelect(index);
22898 * @param {Array} records The records to select
22899 * @param {Boolean} keepExisting (optional) True to keep existing selections
22901 selectRecords : function(records, keepExisting)
22904 this.clearSelections();
22906 var ds = this.grid.store;
22907 for(var i = 0, len = records.length; i < len; i++){
22908 this.selectRow(ds.indexOf(records[i]), true);
22913 * Gets the number of selected rows.
22916 getCount : function(){
22917 return this.selections.length;
22921 * Selects the first row in the grid.
22923 selectFirstRow : function(){
22928 * Select the last row.
22929 * @param {Boolean} keepExisting (optional) True to keep existing selections
22931 selectLastRow : function(keepExisting){
22932 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22933 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22937 * Selects the row immediately following the last selected row.
22938 * @param {Boolean} keepExisting (optional) True to keep existing selections
22940 selectNext : function(keepExisting)
22942 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22943 this.selectRow(this.last+1, keepExisting);
22944 this.grid.getView().focusRow(this.last);
22949 * Selects the row that precedes the last selected row.
22950 * @param {Boolean} keepExisting (optional) True to keep existing selections
22952 selectPrevious : function(keepExisting){
22954 this.selectRow(this.last-1, keepExisting);
22955 this.grid.getView().focusRow(this.last);
22960 * Returns the selected records
22961 * @return {Array} Array of selected records
22963 getSelections : function(){
22964 return [].concat(this.selections.items);
22968 * Returns the first selected record.
22971 getSelected : function(){
22972 return this.selections.itemAt(0);
22977 * Clears all selections.
22979 clearSelections : function(fast)
22985 var ds = this.grid.store;
22986 var s = this.selections;
22987 s.each(function(r){
22988 this.deselectRow(ds.indexOfId(r.id));
22992 this.selections.clear();
22999 * Selects all rows.
23001 selectAll : function(){
23005 this.selections.clear();
23006 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23007 this.selectRow(i, true);
23012 * Returns True if there is a selection.
23013 * @return {Boolean}
23015 hasSelection : function(){
23016 return this.selections.length > 0;
23020 * Returns True if the specified row is selected.
23021 * @param {Number/Record} record The record or index of the record to check
23022 * @return {Boolean}
23024 isSelected : function(index){
23025 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23026 return (r && this.selections.key(r.id) ? true : false);
23030 * Returns True if the specified record id is selected.
23031 * @param {String} id The id of record to check
23032 * @return {Boolean}
23034 isIdSelected : function(id){
23035 return (this.selections.key(id) ? true : false);
23040 handleMouseDBClick : function(e, t){
23044 handleMouseDown : function(e, t)
23046 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23047 if(this.isLocked() || rowIndex < 0 ){
23050 if(e.shiftKey && this.last !== false){
23051 var last = this.last;
23052 this.selectRange(last, rowIndex, e.ctrlKey);
23053 this.last = last; // reset the last
23057 var isSelected = this.isSelected(rowIndex);
23058 //Roo.log("select row:" + rowIndex);
23060 this.deselectRow(rowIndex);
23062 this.selectRow(rowIndex, true);
23066 if(e.button !== 0 && isSelected){
23067 alert('rowIndex 2: ' + rowIndex);
23068 view.focusRow(rowIndex);
23069 }else if(e.ctrlKey && isSelected){
23070 this.deselectRow(rowIndex);
23071 }else if(!isSelected){
23072 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23073 view.focusRow(rowIndex);
23077 this.fireEvent("afterselectionchange", this);
23080 handleDragableRowClick : function(grid, rowIndex, e)
23082 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23083 this.selectRow(rowIndex, false);
23084 grid.view.focusRow(rowIndex);
23085 this.fireEvent("afterselectionchange", this);
23090 * Selects multiple rows.
23091 * @param {Array} rows Array of the indexes of the row to select
23092 * @param {Boolean} keepExisting (optional) True to keep existing selections
23094 selectRows : function(rows, keepExisting){
23096 this.clearSelections();
23098 for(var i = 0, len = rows.length; i < len; i++){
23099 this.selectRow(rows[i], true);
23104 * Selects a range of rows. All rows in between startRow and endRow are also selected.
23105 * @param {Number} startRow The index of the first row in the range
23106 * @param {Number} endRow The index of the last row in the range
23107 * @param {Boolean} keepExisting (optional) True to retain existing selections
23109 selectRange : function(startRow, endRow, keepExisting){
23114 this.clearSelections();
23116 if(startRow <= endRow){
23117 for(var i = startRow; i <= endRow; i++){
23118 this.selectRow(i, true);
23121 for(var i = startRow; i >= endRow; i--){
23122 this.selectRow(i, true);
23128 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23129 * @param {Number} startRow The index of the first row in the range
23130 * @param {Number} endRow The index of the last row in the range
23132 deselectRange : function(startRow, endRow, preventViewNotify){
23136 for(var i = startRow; i <= endRow; i++){
23137 this.deselectRow(i, preventViewNotify);
23143 * @param {Number} row The index of the row to select
23144 * @param {Boolean} keepExisting (optional) True to keep existing selections
23146 selectRow : function(index, keepExisting, preventViewNotify)
23148 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23151 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23152 if(!keepExisting || this.singleSelect){
23153 this.clearSelections();
23156 var r = this.grid.store.getAt(index);
23157 //console.log('selectRow - record id :' + r.id);
23159 this.selections.add(r);
23160 this.last = this.lastActive = index;
23161 if(!preventViewNotify){
23162 var proxy = new Roo.Element(
23163 this.grid.getRowDom(index)
23165 proxy.addClass('bg-info info');
23167 this.fireEvent("rowselect", this, index, r);
23168 this.fireEvent("selectionchange", this);
23174 * @param {Number} row The index of the row to deselect
23176 deselectRow : function(index, preventViewNotify)
23181 if(this.last == index){
23184 if(this.lastActive == index){
23185 this.lastActive = false;
23188 var r = this.grid.store.getAt(index);
23193 this.selections.remove(r);
23194 //.console.log('deselectRow - record id :' + r.id);
23195 if(!preventViewNotify){
23197 var proxy = new Roo.Element(
23198 this.grid.getRowDom(index)
23200 proxy.removeClass('bg-info info');
23202 this.fireEvent("rowdeselect", this, index);
23203 this.fireEvent("selectionchange", this);
23207 restoreLast : function(){
23209 this.last = this._last;
23214 acceptsNav : function(row, col, cm){
23215 return !cm.isHidden(col) && cm.isCellEditable(col, row);
23219 onEditorKey : function(field, e){
23220 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23225 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23227 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23229 }else if(k == e.ENTER && !e.ctrlKey){
23233 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23235 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23237 }else if(k == e.ESC){
23241 g.startEditing(newCell[0], newCell[1]);
23247 * Ext JS Library 1.1.1
23248 * Copyright(c) 2006-2007, Ext JS, LLC.
23250 * Originally Released Under LGPL - original licence link has changed is not relivant.
23253 * <script type="text/javascript">
23257 * @class Roo.bootstrap.PagingToolbar
23258 * @extends Roo.bootstrap.NavSimplebar
23259 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23261 * Create a new PagingToolbar
23262 * @param {Object} config The config object
23263 * @param {Roo.data.Store} store
23265 Roo.bootstrap.PagingToolbar = function(config)
23267 // old args format still supported... - xtype is prefered..
23268 // created from xtype...
23270 this.ds = config.dataSource;
23272 if (config.store && !this.ds) {
23273 this.store= Roo.factory(config.store, Roo.data);
23274 this.ds = this.store;
23275 this.ds.xmodule = this.xmodule || false;
23278 this.toolbarItems = [];
23279 if (config.items) {
23280 this.toolbarItems = config.items;
23283 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23288 this.bind(this.ds);
23291 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23295 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23297 * @cfg {Roo.data.Store} dataSource
23298 * The underlying data store providing the paged data
23301 * @cfg {String/HTMLElement/Element} container
23302 * container The id or element that will contain the toolbar
23305 * @cfg {Boolean} displayInfo
23306 * True to display the displayMsg (defaults to false)
23309 * @cfg {Number} pageSize
23310 * The number of records to display per page (defaults to 20)
23314 * @cfg {String} displayMsg
23315 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23317 displayMsg : 'Displaying {0} - {1} of {2}',
23319 * @cfg {String} emptyMsg
23320 * The message to display when no records are found (defaults to "No data to display")
23322 emptyMsg : 'No data to display',
23324 * Customizable piece of the default paging text (defaults to "Page")
23327 beforePageText : "Page",
23329 * Customizable piece of the default paging text (defaults to "of %0")
23332 afterPageText : "of {0}",
23334 * Customizable piece of the default paging text (defaults to "First Page")
23337 firstText : "First Page",
23339 * Customizable piece of the default paging text (defaults to "Previous Page")
23342 prevText : "Previous Page",
23344 * Customizable piece of the default paging text (defaults to "Next Page")
23347 nextText : "Next Page",
23349 * Customizable piece of the default paging text (defaults to "Last Page")
23352 lastText : "Last Page",
23354 * Customizable piece of the default paging text (defaults to "Refresh")
23357 refreshText : "Refresh",
23361 onRender : function(ct, position)
23363 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23364 this.navgroup.parentId = this.id;
23365 this.navgroup.onRender(this.el, null);
23366 // add the buttons to the navgroup
23368 if(this.displayInfo){
23369 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23370 this.displayEl = this.el.select('.x-paging-info', true).first();
23371 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23372 // this.displayEl = navel.el.select('span',true).first();
23378 Roo.each(_this.buttons, function(e){ // this might need to use render????
23379 Roo.factory(e).onRender(_this.el, null);
23383 Roo.each(_this.toolbarItems, function(e) {
23384 _this.navgroup.addItem(e);
23388 this.first = this.navgroup.addItem({
23389 tooltip: this.firstText,
23391 icon : 'fa fa-backward',
23393 preventDefault: true,
23394 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23397 this.prev = this.navgroup.addItem({
23398 tooltip: this.prevText,
23400 icon : 'fa fa-step-backward',
23402 preventDefault: true,
23403 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
23405 //this.addSeparator();
23408 var field = this.navgroup.addItem( {
23410 cls : 'x-paging-position',
23412 html : this.beforePageText +
23413 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23414 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
23417 this.field = field.el.select('input', true).first();
23418 this.field.on("keydown", this.onPagingKeydown, this);
23419 this.field.on("focus", function(){this.dom.select();});
23422 this.afterTextEl = field.el.select('.x-paging-after',true).first();
23423 //this.field.setHeight(18);
23424 //this.addSeparator();
23425 this.next = this.navgroup.addItem({
23426 tooltip: this.nextText,
23428 html : ' <i class="fa fa-step-forward">',
23430 preventDefault: true,
23431 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
23433 this.last = this.navgroup.addItem({
23434 tooltip: this.lastText,
23435 icon : 'fa fa-forward',
23438 preventDefault: true,
23439 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
23441 //this.addSeparator();
23442 this.loading = this.navgroup.addItem({
23443 tooltip: this.refreshText,
23444 icon: 'fa fa-refresh',
23445 preventDefault: true,
23446 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23452 updateInfo : function(){
23453 if(this.displayEl){
23454 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23455 var msg = count == 0 ?
23459 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
23461 this.displayEl.update(msg);
23466 onLoad : function(ds, r, o){
23467 this.cursor = o.params ? o.params.start : 0;
23468 var d = this.getPageData(),
23472 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23473 this.field.dom.value = ap;
23474 this.first.setDisabled(ap == 1);
23475 this.prev.setDisabled(ap == 1);
23476 this.next.setDisabled(ap == ps);
23477 this.last.setDisabled(ap == ps);
23478 this.loading.enable();
23483 getPageData : function(){
23484 var total = this.ds.getTotalCount();
23487 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23488 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23493 onLoadError : function(){
23494 this.loading.enable();
23498 onPagingKeydown : function(e){
23499 var k = e.getKey();
23500 var d = this.getPageData();
23502 var v = this.field.dom.value, pageNum;
23503 if(!v || isNaN(pageNum = parseInt(v, 10))){
23504 this.field.dom.value = d.activePage;
23507 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23508 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23511 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))
23513 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23514 this.field.dom.value = pageNum;
23515 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23518 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23520 var v = this.field.dom.value, pageNum;
23521 var increment = (e.shiftKey) ? 10 : 1;
23522 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23525 if(!v || isNaN(pageNum = parseInt(v, 10))) {
23526 this.field.dom.value = d.activePage;
23529 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23531 this.field.dom.value = parseInt(v, 10) + increment;
23532 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23533 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23540 beforeLoad : function(){
23542 this.loading.disable();
23547 onClick : function(which){
23556 ds.load({params:{start: 0, limit: this.pageSize}});
23559 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23562 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23565 var total = ds.getTotalCount();
23566 var extra = total % this.pageSize;
23567 var lastStart = extra ? (total - extra) : total-this.pageSize;
23568 ds.load({params:{start: lastStart, limit: this.pageSize}});
23571 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23577 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23578 * @param {Roo.data.Store} store The data store to unbind
23580 unbind : function(ds){
23581 ds.un("beforeload", this.beforeLoad, this);
23582 ds.un("load", this.onLoad, this);
23583 ds.un("loadexception", this.onLoadError, this);
23584 ds.un("remove", this.updateInfo, this);
23585 ds.un("add", this.updateInfo, this);
23586 this.ds = undefined;
23590 * Binds the paging toolbar to the specified {@link Roo.data.Store}
23591 * @param {Roo.data.Store} store The data store to bind
23593 bind : function(ds){
23594 ds.on("beforeload", this.beforeLoad, this);
23595 ds.on("load", this.onLoad, this);
23596 ds.on("loadexception", this.onLoadError, this);
23597 ds.on("remove", this.updateInfo, this);
23598 ds.on("add", this.updateInfo, this);
23609 * @class Roo.bootstrap.MessageBar
23610 * @extends Roo.bootstrap.Component
23611 * Bootstrap MessageBar class
23612 * @cfg {String} html contents of the MessageBar
23613 * @cfg {String} weight (info | success | warning | danger) default info
23614 * @cfg {String} beforeClass insert the bar before the given class
23615 * @cfg {Boolean} closable (true | false) default false
23616 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23619 * Create a new Element
23620 * @param {Object} config The config object
23623 Roo.bootstrap.MessageBar = function(config){
23624 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23627 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
23633 beforeClass: 'bootstrap-sticky-wrap',
23635 getAutoCreate : function(){
23639 cls: 'alert alert-dismissable alert-' + this.weight,
23644 html: this.html || ''
23650 cfg.cls += ' alert-messages-fixed';
23664 onRender : function(ct, position)
23666 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23669 var cfg = Roo.apply({}, this.getAutoCreate());
23673 cfg.cls += ' ' + this.cls;
23676 cfg.style = this.style;
23678 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23680 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23683 this.el.select('>button.close').on('click', this.hide, this);
23689 if (!this.rendered) {
23695 this.fireEvent('show', this);
23701 if (!this.rendered) {
23707 this.fireEvent('hide', this);
23710 update : function()
23712 // var e = this.el.dom.firstChild;
23714 // if(this.closable){
23715 // e = e.nextSibling;
23718 // e.data = this.html || '';
23720 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23736 * @class Roo.bootstrap.Graph
23737 * @extends Roo.bootstrap.Component
23738 * Bootstrap Graph class
23742 @cfg {String} graphtype bar | vbar | pie
23743 @cfg {number} g_x coodinator | centre x (pie)
23744 @cfg {number} g_y coodinator | centre y (pie)
23745 @cfg {number} g_r radius (pie)
23746 @cfg {number} g_height height of the chart (respected by all elements in the set)
23747 @cfg {number} g_width width of the chart (respected by all elements in the set)
23748 @cfg {Object} title The title of the chart
23751 -opts (object) options for the chart
23753 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23754 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23756 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.
23757 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23759 o stretch (boolean)
23761 -opts (object) options for the pie
23764 o startAngle (number)
23765 o endAngle (number)
23769 * Create a new Input
23770 * @param {Object} config The config object
23773 Roo.bootstrap.Graph = function(config){
23774 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23780 * The img click event for the img.
23781 * @param {Roo.EventObject} e
23787 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23798 //g_colors: this.colors,
23805 getAutoCreate : function(){
23816 onRender : function(ct,position){
23819 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23821 if (typeof(Raphael) == 'undefined') {
23822 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23826 this.raphael = Raphael(this.el.dom);
23828 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23829 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23830 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23831 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23833 r.text(160, 10, "Single Series Chart").attr(txtattr);
23834 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23835 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23836 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23838 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23839 r.barchart(330, 10, 300, 220, data1);
23840 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23841 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23844 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23845 // r.barchart(30, 30, 560, 250, xdata, {
23846 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23847 // axis : "0 0 1 1",
23848 // axisxlabels : xdata
23849 // //yvalues : cols,
23852 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23854 // this.load(null,xdata,{
23855 // axis : "0 0 1 1",
23856 // axisxlabels : xdata
23861 load : function(graphtype,xdata,opts)
23863 this.raphael.clear();
23865 graphtype = this.graphtype;
23870 var r = this.raphael,
23871 fin = function () {
23872 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23874 fout = function () {
23875 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23877 pfin = function() {
23878 this.sector.stop();
23879 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23882 this.label[0].stop();
23883 this.label[0].attr({ r: 7.5 });
23884 this.label[1].attr({ "font-weight": 800 });
23887 pfout = function() {
23888 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23891 this.label[0].animate({ r: 5 }, 500, "bounce");
23892 this.label[1].attr({ "font-weight": 400 });
23898 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23901 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23904 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23905 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23907 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23914 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23919 setTitle: function(o)
23924 initEvents: function() {
23927 this.el.on('click', this.onClick, this);
23931 onClick : function(e)
23933 Roo.log('img onclick');
23934 this.fireEvent('click', this, e);
23946 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23949 * @class Roo.bootstrap.dash.NumberBox
23950 * @extends Roo.bootstrap.Component
23951 * Bootstrap NumberBox class
23952 * @cfg {String} headline Box headline
23953 * @cfg {String} content Box content
23954 * @cfg {String} icon Box icon
23955 * @cfg {String} footer Footer text
23956 * @cfg {String} fhref Footer href
23959 * Create a new NumberBox
23960 * @param {Object} config The config object
23964 Roo.bootstrap.dash.NumberBox = function(config){
23965 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23969 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23978 getAutoCreate : function(){
23982 cls : 'small-box ',
23990 cls : 'roo-headline',
23991 html : this.headline
23995 cls : 'roo-content',
23996 html : this.content
24010 cls : 'ion ' + this.icon
24019 cls : 'small-box-footer',
24020 href : this.fhref || '#',
24024 cfg.cn.push(footer);
24031 onRender : function(ct,position){
24032 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24039 setHeadline: function (value)
24041 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24044 setFooter: function (value, href)
24046 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24049 this.el.select('a.small-box-footer',true).first().attr('href', href);
24054 setContent: function (value)
24056 this.el.select('.roo-content',true).first().dom.innerHTML = value;
24059 initEvents: function()
24073 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24076 * @class Roo.bootstrap.dash.TabBox
24077 * @extends Roo.bootstrap.Component
24078 * Bootstrap TabBox class
24079 * @cfg {String} title Title of the TabBox
24080 * @cfg {String} icon Icon of the TabBox
24081 * @cfg {Boolean} showtabs (true|false) show the tabs default true
24082 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24085 * Create a new TabBox
24086 * @param {Object} config The config object
24090 Roo.bootstrap.dash.TabBox = function(config){
24091 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24096 * When a pane is added
24097 * @param {Roo.bootstrap.dash.TabPane} pane
24101 * @event activatepane
24102 * When a pane is activated
24103 * @param {Roo.bootstrap.dash.TabPane} pane
24105 "activatepane" : true
24113 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
24118 tabScrollable : false,
24120 getChildContainer : function()
24122 return this.el.select('.tab-content', true).first();
24125 getAutoCreate : function(){
24129 cls: 'pull-left header',
24137 cls: 'fa ' + this.icon
24143 cls: 'nav nav-tabs pull-right',
24149 if(this.tabScrollable){
24156 cls: 'nav nav-tabs pull-right',
24167 cls: 'nav-tabs-custom',
24172 cls: 'tab-content no-padding',
24180 initEvents : function()
24182 //Roo.log('add add pane handler');
24183 this.on('addpane', this.onAddPane, this);
24186 * Updates the box title
24187 * @param {String} html to set the title to.
24189 setTitle : function(value)
24191 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24193 onAddPane : function(pane)
24195 this.panes.push(pane);
24196 //Roo.log('addpane');
24198 // tabs are rendere left to right..
24199 if(!this.showtabs){
24203 var ctr = this.el.select('.nav-tabs', true).first();
24206 var existing = ctr.select('.nav-tab',true);
24207 var qty = existing.getCount();;
24210 var tab = ctr.createChild({
24212 cls : 'nav-tab' + (qty ? '' : ' active'),
24220 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24223 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24225 pane.el.addClass('active');
24230 onTabClick : function(ev,un,ob,pane)
24232 //Roo.log('tab - prev default');
24233 ev.preventDefault();
24236 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24237 pane.tab.addClass('active');
24238 //Roo.log(pane.title);
24239 this.getChildContainer().select('.tab-pane',true).removeClass('active');
24240 // technically we should have a deactivate event.. but maybe add later.
24241 // and it should not de-activate the selected tab...
24242 this.fireEvent('activatepane', pane);
24243 pane.el.addClass('active');
24244 pane.fireEvent('activate');
24249 getActivePane : function()
24252 Roo.each(this.panes, function(p) {
24253 if(p.el.hasClass('active')){
24274 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24276 * @class Roo.bootstrap.TabPane
24277 * @extends Roo.bootstrap.Component
24278 * Bootstrap TabPane class
24279 * @cfg {Boolean} active (false | true) Default false
24280 * @cfg {String} title title of panel
24284 * Create a new TabPane
24285 * @param {Object} config The config object
24288 Roo.bootstrap.dash.TabPane = function(config){
24289 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24295 * When a pane is activated
24296 * @param {Roo.bootstrap.dash.TabPane} pane
24303 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
24308 // the tabBox that this is attached to.
24311 getAutoCreate : function()
24319 cfg.cls += ' active';
24324 initEvents : function()
24326 //Roo.log('trigger add pane handler');
24327 this.parent().fireEvent('addpane', this)
24331 * Updates the tab title
24332 * @param {String} html to set the title to.
24334 setTitle: function(str)
24340 this.tab.select('a', true).first().dom.innerHTML = str;
24357 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24360 * @class Roo.bootstrap.menu.Menu
24361 * @extends Roo.bootstrap.Component
24362 * Bootstrap Menu class - container for Menu
24363 * @cfg {String} html Text of the menu
24364 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24365 * @cfg {String} icon Font awesome icon
24366 * @cfg {String} pos Menu align to (top | bottom) default bottom
24370 * Create a new Menu
24371 * @param {Object} config The config object
24375 Roo.bootstrap.menu.Menu = function(config){
24376 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24380 * @event beforeshow
24381 * Fires before this menu is displayed
24382 * @param {Roo.bootstrap.menu.Menu} this
24386 * @event beforehide
24387 * Fires before this menu is hidden
24388 * @param {Roo.bootstrap.menu.Menu} this
24393 * Fires after this menu is displayed
24394 * @param {Roo.bootstrap.menu.Menu} this
24399 * Fires after this menu is hidden
24400 * @param {Roo.bootstrap.menu.Menu} this
24405 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24406 * @param {Roo.bootstrap.menu.Menu} this
24407 * @param {Roo.EventObject} e
24414 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
24418 weight : 'default',
24423 getChildContainer : function() {
24424 if(this.isSubMenu){
24428 return this.el.select('ul.dropdown-menu', true).first();
24431 getAutoCreate : function()
24436 cls : 'roo-menu-text',
24444 cls : 'fa ' + this.icon
24455 cls : 'dropdown-button btn btn-' + this.weight,
24460 cls : 'dropdown-toggle btn btn-' + this.weight,
24470 cls : 'dropdown-menu'
24476 if(this.pos == 'top'){
24477 cfg.cls += ' dropup';
24480 if(this.isSubMenu){
24483 cls : 'dropdown-menu'
24490 onRender : function(ct, position)
24492 this.isSubMenu = ct.hasClass('dropdown-submenu');
24494 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24497 initEvents : function()
24499 if(this.isSubMenu){
24503 this.hidden = true;
24505 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24506 this.triggerEl.on('click', this.onTriggerPress, this);
24508 this.buttonEl = this.el.select('button.dropdown-button', true).first();
24509 this.buttonEl.on('click', this.onClick, this);
24515 if(this.isSubMenu){
24519 return this.el.select('ul.dropdown-menu', true).first();
24522 onClick : function(e)
24524 this.fireEvent("click", this, e);
24527 onTriggerPress : function(e)
24529 if (this.isVisible()) {
24536 isVisible : function(){
24537 return !this.hidden;
24542 this.fireEvent("beforeshow", this);
24544 this.hidden = false;
24545 this.el.addClass('open');
24547 Roo.get(document).on("mouseup", this.onMouseUp, this);
24549 this.fireEvent("show", this);
24556 this.fireEvent("beforehide", this);
24558 this.hidden = true;
24559 this.el.removeClass('open');
24561 Roo.get(document).un("mouseup", this.onMouseUp);
24563 this.fireEvent("hide", this);
24566 onMouseUp : function()
24580 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24583 * @class Roo.bootstrap.menu.Item
24584 * @extends Roo.bootstrap.Component
24585 * Bootstrap MenuItem class
24586 * @cfg {Boolean} submenu (true | false) default false
24587 * @cfg {String} html text of the item
24588 * @cfg {String} href the link
24589 * @cfg {Boolean} disable (true | false) default false
24590 * @cfg {Boolean} preventDefault (true | false) default true
24591 * @cfg {String} icon Font awesome icon
24592 * @cfg {String} pos Submenu align to (left | right) default right
24596 * Create a new Item
24597 * @param {Object} config The config object
24601 Roo.bootstrap.menu.Item = function(config){
24602 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24606 * Fires when the mouse is hovering over this menu
24607 * @param {Roo.bootstrap.menu.Item} this
24608 * @param {Roo.EventObject} e
24613 * Fires when the mouse exits this menu
24614 * @param {Roo.bootstrap.menu.Item} this
24615 * @param {Roo.EventObject} e
24621 * The raw click event for the entire grid.
24622 * @param {Roo.EventObject} e
24628 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
24633 preventDefault: true,
24638 getAutoCreate : function()
24643 cls : 'roo-menu-item-text',
24651 cls : 'fa ' + this.icon
24660 href : this.href || '#',
24667 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24671 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24673 if(this.pos == 'left'){
24674 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24681 initEvents : function()
24683 this.el.on('mouseover', this.onMouseOver, this);
24684 this.el.on('mouseout', this.onMouseOut, this);
24686 this.el.select('a', true).first().on('click', this.onClick, this);
24690 onClick : function(e)
24692 if(this.preventDefault){
24693 e.preventDefault();
24696 this.fireEvent("click", this, e);
24699 onMouseOver : function(e)
24701 if(this.submenu && this.pos == 'left'){
24702 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24705 this.fireEvent("mouseover", this, e);
24708 onMouseOut : function(e)
24710 this.fireEvent("mouseout", this, e);
24722 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24725 * @class Roo.bootstrap.menu.Separator
24726 * @extends Roo.bootstrap.Component
24727 * Bootstrap Separator class
24730 * Create a new Separator
24731 * @param {Object} config The config object
24735 Roo.bootstrap.menu.Separator = function(config){
24736 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24739 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24741 getAutoCreate : function(){
24762 * @class Roo.bootstrap.Tooltip
24763 * Bootstrap Tooltip class
24764 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24765 * to determine which dom element triggers the tooltip.
24767 * It needs to add support for additional attributes like tooltip-position
24770 * Create a new Toolti
24771 * @param {Object} config The config object
24774 Roo.bootstrap.Tooltip = function(config){
24775 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24778 Roo.apply(Roo.bootstrap.Tooltip, {
24780 * @function init initialize tooltip monitoring.
24784 currentTip : false,
24785 currentRegion : false,
24791 Roo.get(document).on('mouseover', this.enter ,this);
24792 Roo.get(document).on('mouseout', this.leave, this);
24795 this.currentTip = new Roo.bootstrap.Tooltip();
24798 enter : function(ev)
24800 var dom = ev.getTarget();
24802 //Roo.log(['enter',dom]);
24803 var el = Roo.fly(dom);
24804 if (this.currentEl) {
24806 //Roo.log(this.currentEl);
24807 //Roo.log(this.currentEl.contains(dom));
24808 if (this.currentEl == el) {
24811 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24817 if (this.currentTip.el) {
24818 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24822 if(!el || el.dom == document){
24828 // you can not look for children, as if el is the body.. then everythign is the child..
24829 if (!el.attr('tooltip')) { //
24830 if (!el.select("[tooltip]").elements.length) {
24833 // is the mouse over this child...?
24834 bindEl = el.select("[tooltip]").first();
24835 var xy = ev.getXY();
24836 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24837 //Roo.log("not in region.");
24840 //Roo.log("child element over..");
24843 this.currentEl = bindEl;
24844 this.currentTip.bind(bindEl);
24845 this.currentRegion = Roo.lib.Region.getRegion(dom);
24846 this.currentTip.enter();
24849 leave : function(ev)
24851 var dom = ev.getTarget();
24852 //Roo.log(['leave',dom]);
24853 if (!this.currentEl) {
24858 if (dom != this.currentEl.dom) {
24861 var xy = ev.getXY();
24862 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24865 // only activate leave if mouse cursor is outside... bounding box..
24870 if (this.currentTip) {
24871 this.currentTip.leave();
24873 //Roo.log('clear currentEl');
24874 this.currentEl = false;
24879 'left' : ['r-l', [-2,0], 'right'],
24880 'right' : ['l-r', [2,0], 'left'],
24881 'bottom' : ['t-b', [0,2], 'top'],
24882 'top' : [ 'b-t', [0,-2], 'bottom']
24888 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24893 delay : null, // can be { show : 300 , hide: 500}
24897 hoverState : null, //???
24899 placement : 'bottom',
24901 getAutoCreate : function(){
24908 cls : 'tooltip-arrow'
24911 cls : 'tooltip-inner'
24918 bind : function(el)
24924 enter : function () {
24926 if (this.timeout != null) {
24927 clearTimeout(this.timeout);
24930 this.hoverState = 'in';
24931 //Roo.log("enter - show");
24932 if (!this.delay || !this.delay.show) {
24937 this.timeout = setTimeout(function () {
24938 if (_t.hoverState == 'in') {
24941 }, this.delay.show);
24945 clearTimeout(this.timeout);
24947 this.hoverState = 'out';
24948 if (!this.delay || !this.delay.hide) {
24954 this.timeout = setTimeout(function () {
24955 //Roo.log("leave - timeout");
24957 if (_t.hoverState == 'out') {
24959 Roo.bootstrap.Tooltip.currentEl = false;
24967 this.render(document.body);
24970 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24972 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24974 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24976 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24978 var placement = typeof this.placement == 'function' ?
24979 this.placement.call(this, this.el, on_el) :
24982 var autoToken = /\s?auto?\s?/i;
24983 var autoPlace = autoToken.test(placement);
24985 placement = placement.replace(autoToken, '') || 'top';
24989 //this.el.setXY([0,0]);
24991 //this.el.dom.style.display='block';
24993 //this.el.appendTo(on_el);
24995 var p = this.getPosition();
24996 var box = this.el.getBox();
25002 var align = Roo.bootstrap.Tooltip.alignment[placement];
25004 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25006 if(placement == 'top' || placement == 'bottom'){
25008 placement = 'right';
25011 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25012 placement = 'left';
25015 var scroll = Roo.select('body', true).first().getScroll();
25017 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25023 align = Roo.bootstrap.Tooltip.alignment[placement];
25025 this.el.alignTo(this.bindEl, align[0],align[1]);
25026 //var arrow = this.el.select('.arrow',true).first();
25027 //arrow.set(align[2],
25029 this.el.addClass(placement);
25031 this.el.addClass('in fade');
25033 this.hoverState = null;
25035 if (this.el.hasClass('fade')) {
25046 //this.el.setXY([0,0]);
25047 this.el.removeClass('in');
25063 * @class Roo.bootstrap.LocationPicker
25064 * @extends Roo.bootstrap.Component
25065 * Bootstrap LocationPicker class
25066 * @cfg {Number} latitude Position when init default 0
25067 * @cfg {Number} longitude Position when init default 0
25068 * @cfg {Number} zoom default 15
25069 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25070 * @cfg {Boolean} mapTypeControl default false
25071 * @cfg {Boolean} disableDoubleClickZoom default false
25072 * @cfg {Boolean} scrollwheel default true
25073 * @cfg {Boolean} streetViewControl default false
25074 * @cfg {Number} radius default 0
25075 * @cfg {String} locationName
25076 * @cfg {Boolean} draggable default true
25077 * @cfg {Boolean} enableAutocomplete default false
25078 * @cfg {Boolean} enableReverseGeocode default true
25079 * @cfg {String} markerTitle
25082 * Create a new LocationPicker
25083 * @param {Object} config The config object
25087 Roo.bootstrap.LocationPicker = function(config){
25089 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25094 * Fires when the picker initialized.
25095 * @param {Roo.bootstrap.LocationPicker} this
25096 * @param {Google Location} location
25100 * @event positionchanged
25101 * Fires when the picker position changed.
25102 * @param {Roo.bootstrap.LocationPicker} this
25103 * @param {Google Location} location
25105 positionchanged : true,
25108 * Fires when the map resize.
25109 * @param {Roo.bootstrap.LocationPicker} this
25114 * Fires when the map show.
25115 * @param {Roo.bootstrap.LocationPicker} this
25120 * Fires when the map hide.
25121 * @param {Roo.bootstrap.LocationPicker} this
25126 * Fires when click the map.
25127 * @param {Roo.bootstrap.LocationPicker} this
25128 * @param {Map event} e
25132 * @event mapRightClick
25133 * Fires when right click the map.
25134 * @param {Roo.bootstrap.LocationPicker} this
25135 * @param {Map event} e
25137 mapRightClick : true,
25139 * @event markerClick
25140 * Fires when click the marker.
25141 * @param {Roo.bootstrap.LocationPicker} this
25142 * @param {Map event} e
25144 markerClick : true,
25146 * @event markerRightClick
25147 * Fires when right click the marker.
25148 * @param {Roo.bootstrap.LocationPicker} this
25149 * @param {Map event} e
25151 markerRightClick : true,
25153 * @event OverlayViewDraw
25154 * Fires when OverlayView Draw
25155 * @param {Roo.bootstrap.LocationPicker} this
25157 OverlayViewDraw : true,
25159 * @event OverlayViewOnAdd
25160 * Fires when OverlayView Draw
25161 * @param {Roo.bootstrap.LocationPicker} this
25163 OverlayViewOnAdd : true,
25165 * @event OverlayViewOnRemove
25166 * Fires when OverlayView Draw
25167 * @param {Roo.bootstrap.LocationPicker} this
25169 OverlayViewOnRemove : true,
25171 * @event OverlayViewShow
25172 * Fires when OverlayView Draw
25173 * @param {Roo.bootstrap.LocationPicker} this
25174 * @param {Pixel} cpx
25176 OverlayViewShow : true,
25178 * @event OverlayViewHide
25179 * Fires when OverlayView Draw
25180 * @param {Roo.bootstrap.LocationPicker} this
25182 OverlayViewHide : true,
25184 * @event loadexception
25185 * Fires when load google lib failed.
25186 * @param {Roo.bootstrap.LocationPicker} this
25188 loadexception : true
25193 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
25195 gMapContext: false,
25201 mapTypeControl: false,
25202 disableDoubleClickZoom: false,
25204 streetViewControl: false,
25208 enableAutocomplete: false,
25209 enableReverseGeocode: true,
25212 getAutoCreate: function()
25217 cls: 'roo-location-picker'
25223 initEvents: function(ct, position)
25225 if(!this.el.getWidth() || this.isApplied()){
25229 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25234 initial: function()
25236 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25237 this.fireEvent('loadexception', this);
25241 if(!this.mapTypeId){
25242 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25245 this.gMapContext = this.GMapContext();
25247 this.initOverlayView();
25249 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25253 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25254 _this.setPosition(_this.gMapContext.marker.position);
25257 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25258 _this.fireEvent('mapClick', this, event);
25262 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25263 _this.fireEvent('mapRightClick', this, event);
25267 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25268 _this.fireEvent('markerClick', this, event);
25272 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25273 _this.fireEvent('markerRightClick', this, event);
25277 this.setPosition(this.gMapContext.location);
25279 this.fireEvent('initial', this, this.gMapContext.location);
25282 initOverlayView: function()
25286 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25290 _this.fireEvent('OverlayViewDraw', _this);
25295 _this.fireEvent('OverlayViewOnAdd', _this);
25298 onRemove: function()
25300 _this.fireEvent('OverlayViewOnRemove', _this);
25303 show: function(cpx)
25305 _this.fireEvent('OverlayViewShow', _this, cpx);
25310 _this.fireEvent('OverlayViewHide', _this);
25316 fromLatLngToContainerPixel: function(event)
25318 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25321 isApplied: function()
25323 return this.getGmapContext() == false ? false : true;
25326 getGmapContext: function()
25328 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25331 GMapContext: function()
25333 var position = new google.maps.LatLng(this.latitude, this.longitude);
25335 var _map = new google.maps.Map(this.el.dom, {
25338 mapTypeId: this.mapTypeId,
25339 mapTypeControl: this.mapTypeControl,
25340 disableDoubleClickZoom: this.disableDoubleClickZoom,
25341 scrollwheel: this.scrollwheel,
25342 streetViewControl: this.streetViewControl,
25343 locationName: this.locationName,
25344 draggable: this.draggable,
25345 enableAutocomplete: this.enableAutocomplete,
25346 enableReverseGeocode: this.enableReverseGeocode
25349 var _marker = new google.maps.Marker({
25350 position: position,
25352 title: this.markerTitle,
25353 draggable: this.draggable
25360 location: position,
25361 radius: this.radius,
25362 locationName: this.locationName,
25363 addressComponents: {
25364 formatted_address: null,
25365 addressLine1: null,
25366 addressLine2: null,
25368 streetNumber: null,
25372 stateOrProvince: null
25375 domContainer: this.el.dom,
25376 geodecoder: new google.maps.Geocoder()
25380 drawCircle: function(center, radius, options)
25382 if (this.gMapContext.circle != null) {
25383 this.gMapContext.circle.setMap(null);
25387 options = Roo.apply({}, options, {
25388 strokeColor: "#0000FF",
25389 strokeOpacity: .35,
25391 fillColor: "#0000FF",
25395 options.map = this.gMapContext.map;
25396 options.radius = radius;
25397 options.center = center;
25398 this.gMapContext.circle = new google.maps.Circle(options);
25399 return this.gMapContext.circle;
25405 setPosition: function(location)
25407 this.gMapContext.location = location;
25408 this.gMapContext.marker.setPosition(location);
25409 this.gMapContext.map.panTo(location);
25410 this.drawCircle(location, this.gMapContext.radius, {});
25414 if (this.gMapContext.settings.enableReverseGeocode) {
25415 this.gMapContext.geodecoder.geocode({
25416 latLng: this.gMapContext.location
25417 }, function(results, status) {
25419 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25420 _this.gMapContext.locationName = results[0].formatted_address;
25421 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25423 _this.fireEvent('positionchanged', this, location);
25430 this.fireEvent('positionchanged', this, location);
25435 google.maps.event.trigger(this.gMapContext.map, "resize");
25437 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25439 this.fireEvent('resize', this);
25442 setPositionByLatLng: function(latitude, longitude)
25444 this.setPosition(new google.maps.LatLng(latitude, longitude));
25447 getCurrentPosition: function()
25450 latitude: this.gMapContext.location.lat(),
25451 longitude: this.gMapContext.location.lng()
25455 getAddressName: function()
25457 return this.gMapContext.locationName;
25460 getAddressComponents: function()
25462 return this.gMapContext.addressComponents;
25465 address_component_from_google_geocode: function(address_components)
25469 for (var i = 0; i < address_components.length; i++) {
25470 var component = address_components[i];
25471 if (component.types.indexOf("postal_code") >= 0) {
25472 result.postalCode = component.short_name;
25473 } else if (component.types.indexOf("street_number") >= 0) {
25474 result.streetNumber = component.short_name;
25475 } else if (component.types.indexOf("route") >= 0) {
25476 result.streetName = component.short_name;
25477 } else if (component.types.indexOf("neighborhood") >= 0) {
25478 result.city = component.short_name;
25479 } else if (component.types.indexOf("locality") >= 0) {
25480 result.city = component.short_name;
25481 } else if (component.types.indexOf("sublocality") >= 0) {
25482 result.district = component.short_name;
25483 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25484 result.stateOrProvince = component.short_name;
25485 } else if (component.types.indexOf("country") >= 0) {
25486 result.country = component.short_name;
25490 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25491 result.addressLine2 = "";
25495 setZoomLevel: function(zoom)
25497 this.gMapContext.map.setZoom(zoom);
25510 this.fireEvent('show', this);
25521 this.fireEvent('hide', this);
25526 Roo.apply(Roo.bootstrap.LocationPicker, {
25528 OverlayView : function(map, options)
25530 options = options || {};
25544 * @class Roo.bootstrap.Alert
25545 * @extends Roo.bootstrap.Component
25546 * Bootstrap Alert class
25547 * @cfg {String} title The title of alert
25548 * @cfg {String} html The content of alert
25549 * @cfg {String} weight ( success | info | warning | danger )
25550 * @cfg {String} faicon font-awesomeicon
25553 * Create a new alert
25554 * @param {Object} config The config object
25558 Roo.bootstrap.Alert = function(config){
25559 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25563 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
25570 getAutoCreate : function()
25579 cls : 'roo-alert-icon'
25584 cls : 'roo-alert-title',
25589 cls : 'roo-alert-text',
25596 cfg.cn[0].cls += ' fa ' + this.faicon;
25600 cfg.cls += ' alert-' + this.weight;
25606 initEvents: function()
25608 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25611 setTitle : function(str)
25613 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25616 setText : function(str)
25618 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25621 setWeight : function(weight)
25624 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25627 this.weight = weight;
25629 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25632 setIcon : function(icon)
25635 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25638 this.faicon = icon;
25640 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25661 * @class Roo.bootstrap.UploadCropbox
25662 * @extends Roo.bootstrap.Component
25663 * Bootstrap UploadCropbox class
25664 * @cfg {String} emptyText show when image has been loaded
25665 * @cfg {String} rotateNotify show when image too small to rotate
25666 * @cfg {Number} errorTimeout default 3000
25667 * @cfg {Number} minWidth default 300
25668 * @cfg {Number} minHeight default 300
25669 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25670 * @cfg {Boolean} isDocument (true|false) default false
25671 * @cfg {String} url action url
25672 * @cfg {String} paramName default 'imageUpload'
25673 * @cfg {String} method default POST
25674 * @cfg {Boolean} loadMask (true|false) default true
25675 * @cfg {Boolean} loadingText default 'Loading...'
25678 * Create a new UploadCropbox
25679 * @param {Object} config The config object
25682 Roo.bootstrap.UploadCropbox = function(config){
25683 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25687 * @event beforeselectfile
25688 * Fire before select file
25689 * @param {Roo.bootstrap.UploadCropbox} this
25691 "beforeselectfile" : true,
25694 * Fire after initEvent
25695 * @param {Roo.bootstrap.UploadCropbox} this
25700 * Fire after initEvent
25701 * @param {Roo.bootstrap.UploadCropbox} this
25702 * @param {String} data
25707 * Fire when preparing the file data
25708 * @param {Roo.bootstrap.UploadCropbox} this
25709 * @param {Object} file
25714 * Fire when get exception
25715 * @param {Roo.bootstrap.UploadCropbox} this
25716 * @param {XMLHttpRequest} xhr
25718 "exception" : true,
25720 * @event beforeloadcanvas
25721 * Fire before load the canvas
25722 * @param {Roo.bootstrap.UploadCropbox} this
25723 * @param {String} src
25725 "beforeloadcanvas" : true,
25728 * Fire when trash image
25729 * @param {Roo.bootstrap.UploadCropbox} this
25734 * Fire when download the image
25735 * @param {Roo.bootstrap.UploadCropbox} this
25739 * @event footerbuttonclick
25740 * Fire when footerbuttonclick
25741 * @param {Roo.bootstrap.UploadCropbox} this
25742 * @param {String} type
25744 "footerbuttonclick" : true,
25748 * @param {Roo.bootstrap.UploadCropbox} this
25753 * Fire when rotate the image
25754 * @param {Roo.bootstrap.UploadCropbox} this
25755 * @param {String} pos
25760 * Fire when inspect the file
25761 * @param {Roo.bootstrap.UploadCropbox} this
25762 * @param {Object} file
25767 * Fire when xhr upload the file
25768 * @param {Roo.bootstrap.UploadCropbox} this
25769 * @param {Object} data
25774 * Fire when arrange the file data
25775 * @param {Roo.bootstrap.UploadCropbox} this
25776 * @param {Object} formData
25781 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25784 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25786 emptyText : 'Click to upload image',
25787 rotateNotify : 'Image is too small to rotate',
25788 errorTimeout : 3000,
25802 cropType : 'image/jpeg',
25804 canvasLoaded : false,
25805 isDocument : false,
25807 paramName : 'imageUpload',
25809 loadingText : 'Loading...',
25812 getAutoCreate : function()
25816 cls : 'roo-upload-cropbox',
25820 cls : 'roo-upload-cropbox-selector',
25825 cls : 'roo-upload-cropbox-body',
25826 style : 'cursor:pointer',
25830 cls : 'roo-upload-cropbox-preview'
25834 cls : 'roo-upload-cropbox-thumb'
25838 cls : 'roo-upload-cropbox-empty-notify',
25839 html : this.emptyText
25843 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25844 html : this.rotateNotify
25850 cls : 'roo-upload-cropbox-footer',
25853 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25863 onRender : function(ct, position)
25865 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25867 if (this.buttons.length) {
25869 Roo.each(this.buttons, function(bb) {
25871 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25873 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25879 this.maskEl = this.el;
25883 initEvents : function()
25885 this.urlAPI = (window.createObjectURL && window) ||
25886 (window.URL && URL.revokeObjectURL && URL) ||
25887 (window.webkitURL && webkitURL);
25889 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25890 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25892 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25893 this.selectorEl.hide();
25895 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25896 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25898 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25899 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25900 this.thumbEl.hide();
25902 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25903 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25905 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25906 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25907 this.errorEl.hide();
25909 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25910 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25911 this.footerEl.hide();
25913 this.setThumbBoxSize();
25919 this.fireEvent('initial', this);
25926 window.addEventListener("resize", function() { _this.resize(); } );
25928 this.bodyEl.on('click', this.beforeSelectFile, this);
25931 this.bodyEl.on('touchstart', this.onTouchStart, this);
25932 this.bodyEl.on('touchmove', this.onTouchMove, this);
25933 this.bodyEl.on('touchend', this.onTouchEnd, this);
25937 this.bodyEl.on('mousedown', this.onMouseDown, this);
25938 this.bodyEl.on('mousemove', this.onMouseMove, this);
25939 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25940 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25941 Roo.get(document).on('mouseup', this.onMouseUp, this);
25944 this.selectorEl.on('change', this.onFileSelected, this);
25950 this.baseScale = 1;
25952 this.baseRotate = 1;
25953 this.dragable = false;
25954 this.pinching = false;
25957 this.cropData = false;
25958 this.notifyEl.dom.innerHTML = this.emptyText;
25960 this.selectorEl.dom.value = '';
25964 resize : function()
25966 if(this.fireEvent('resize', this) != false){
25967 this.setThumbBoxPosition();
25968 this.setCanvasPosition();
25972 onFooterButtonClick : function(e, el, o, type)
25975 case 'rotate-left' :
25976 this.onRotateLeft(e);
25978 case 'rotate-right' :
25979 this.onRotateRight(e);
25982 this.beforeSelectFile(e);
25997 this.fireEvent('footerbuttonclick', this, type);
26000 beforeSelectFile : function(e)
26002 e.preventDefault();
26004 if(this.fireEvent('beforeselectfile', this) != false){
26005 this.selectorEl.dom.click();
26009 onFileSelected : function(e)
26011 e.preventDefault();
26013 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26017 var file = this.selectorEl.dom.files[0];
26019 if(this.fireEvent('inspect', this, file) != false){
26020 this.prepare(file);
26025 trash : function(e)
26027 this.fireEvent('trash', this);
26030 download : function(e)
26032 this.fireEvent('download', this);
26035 loadCanvas : function(src)
26037 if(this.fireEvent('beforeloadcanvas', this, src) != false){
26041 this.imageEl = document.createElement('img');
26045 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26047 this.imageEl.src = src;
26051 onLoadCanvas : function()
26053 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26054 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26056 this.bodyEl.un('click', this.beforeSelectFile, this);
26058 this.notifyEl.hide();
26059 this.thumbEl.show();
26060 this.footerEl.show();
26062 this.baseRotateLevel();
26064 if(this.isDocument){
26065 this.setThumbBoxSize();
26068 this.setThumbBoxPosition();
26070 this.baseScaleLevel();
26076 this.canvasLoaded = true;
26079 this.maskEl.unmask();
26084 setCanvasPosition : function()
26086 if(!this.canvasEl){
26090 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26091 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26093 this.previewEl.setLeft(pw);
26094 this.previewEl.setTop(ph);
26098 onMouseDown : function(e)
26102 this.dragable = true;
26103 this.pinching = false;
26105 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26106 this.dragable = false;
26110 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26111 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26115 onMouseMove : function(e)
26119 if(!this.canvasLoaded){
26123 if (!this.dragable){
26127 var minX = Math.ceil(this.thumbEl.getLeft(true));
26128 var minY = Math.ceil(this.thumbEl.getTop(true));
26130 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26131 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26133 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26134 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26136 x = x - this.mouseX;
26137 y = y - this.mouseY;
26139 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26140 var bgY = Math.ceil(y + this.previewEl.getTop(true));
26142 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26143 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26145 this.previewEl.setLeft(bgX);
26146 this.previewEl.setTop(bgY);
26148 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26149 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26152 onMouseUp : function(e)
26156 this.dragable = false;
26159 onMouseWheel : function(e)
26163 this.startScale = this.scale;
26165 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26167 if(!this.zoomable()){
26168 this.scale = this.startScale;
26177 zoomable : function()
26179 var minScale = this.thumbEl.getWidth() / this.minWidth;
26181 if(this.minWidth < this.minHeight){
26182 minScale = this.thumbEl.getHeight() / this.minHeight;
26185 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26186 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26190 (this.rotate == 0 || this.rotate == 180) &&
26192 width > this.imageEl.OriginWidth ||
26193 height > this.imageEl.OriginHeight ||
26194 (width < this.minWidth && height < this.minHeight)
26202 (this.rotate == 90 || this.rotate == 270) &&
26204 width > this.imageEl.OriginWidth ||
26205 height > this.imageEl.OriginHeight ||
26206 (width < this.minHeight && height < this.minWidth)
26213 !this.isDocument &&
26214 (this.rotate == 0 || this.rotate == 180) &&
26216 width < this.minWidth ||
26217 width > this.imageEl.OriginWidth ||
26218 height < this.minHeight ||
26219 height > this.imageEl.OriginHeight
26226 !this.isDocument &&
26227 (this.rotate == 90 || this.rotate == 270) &&
26229 width < this.minHeight ||
26230 width > this.imageEl.OriginWidth ||
26231 height < this.minWidth ||
26232 height > this.imageEl.OriginHeight
26242 onRotateLeft : function(e)
26244 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26246 var minScale = this.thumbEl.getWidth() / this.minWidth;
26248 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26249 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26251 this.startScale = this.scale;
26253 while (this.getScaleLevel() < minScale){
26255 this.scale = this.scale + 1;
26257 if(!this.zoomable()){
26262 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26263 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26268 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26275 this.scale = this.startScale;
26277 this.onRotateFail();
26282 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26284 if(this.isDocument){
26285 this.setThumbBoxSize();
26286 this.setThumbBoxPosition();
26287 this.setCanvasPosition();
26292 this.fireEvent('rotate', this, 'left');
26296 onRotateRight : function(e)
26298 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26300 var minScale = this.thumbEl.getWidth() / this.minWidth;
26302 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26303 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26305 this.startScale = this.scale;
26307 while (this.getScaleLevel() < minScale){
26309 this.scale = this.scale + 1;
26311 if(!this.zoomable()){
26316 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26317 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26322 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26329 this.scale = this.startScale;
26331 this.onRotateFail();
26336 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26338 if(this.isDocument){
26339 this.setThumbBoxSize();
26340 this.setThumbBoxPosition();
26341 this.setCanvasPosition();
26346 this.fireEvent('rotate', this, 'right');
26349 onRotateFail : function()
26351 this.errorEl.show(true);
26355 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26360 this.previewEl.dom.innerHTML = '';
26362 var canvasEl = document.createElement("canvas");
26364 var contextEl = canvasEl.getContext("2d");
26366 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26367 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26368 var center = this.imageEl.OriginWidth / 2;
26370 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26371 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26372 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26373 center = this.imageEl.OriginHeight / 2;
26376 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26378 contextEl.translate(center, center);
26379 contextEl.rotate(this.rotate * Math.PI / 180);
26381 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26383 this.canvasEl = document.createElement("canvas");
26385 this.contextEl = this.canvasEl.getContext("2d");
26387 switch (this.rotate) {
26390 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26391 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26393 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26398 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26399 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26401 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26402 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);
26406 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26411 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26412 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26414 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26415 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);
26419 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);
26424 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26425 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26427 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26428 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26432 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);
26439 this.previewEl.appendChild(this.canvasEl);
26441 this.setCanvasPosition();
26446 if(!this.canvasLoaded){
26450 var imageCanvas = document.createElement("canvas");
26452 var imageContext = imageCanvas.getContext("2d");
26454 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26455 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26457 var center = imageCanvas.width / 2;
26459 imageContext.translate(center, center);
26461 imageContext.rotate(this.rotate * Math.PI / 180);
26463 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26465 var canvas = document.createElement("canvas");
26467 var context = canvas.getContext("2d");
26469 canvas.width = this.minWidth;
26470 canvas.height = this.minHeight;
26472 switch (this.rotate) {
26475 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26476 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26478 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26479 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26481 var targetWidth = this.minWidth - 2 * x;
26482 var targetHeight = this.minHeight - 2 * y;
26486 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26487 scale = targetWidth / width;
26490 if(x > 0 && y == 0){
26491 scale = targetHeight / height;
26494 if(x > 0 && y > 0){
26495 scale = targetWidth / width;
26497 if(width < height){
26498 scale = targetHeight / height;
26502 context.scale(scale, scale);
26504 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26505 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26507 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26508 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26510 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26515 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26516 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26518 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26519 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26521 var targetWidth = this.minWidth - 2 * x;
26522 var targetHeight = this.minHeight - 2 * y;
26526 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26527 scale = targetWidth / width;
26530 if(x > 0 && y == 0){
26531 scale = targetHeight / height;
26534 if(x > 0 && y > 0){
26535 scale = targetWidth / width;
26537 if(width < height){
26538 scale = targetHeight / height;
26542 context.scale(scale, scale);
26544 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26545 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26547 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26548 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26550 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26552 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26557 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26558 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26560 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26561 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26563 var targetWidth = this.minWidth - 2 * x;
26564 var targetHeight = this.minHeight - 2 * y;
26568 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26569 scale = targetWidth / width;
26572 if(x > 0 && y == 0){
26573 scale = targetHeight / height;
26576 if(x > 0 && y > 0){
26577 scale = targetWidth / width;
26579 if(width < height){
26580 scale = targetHeight / height;
26584 context.scale(scale, scale);
26586 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26587 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26589 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26590 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26592 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26593 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26595 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26600 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26601 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26603 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26604 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26606 var targetWidth = this.minWidth - 2 * x;
26607 var targetHeight = this.minHeight - 2 * y;
26611 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26612 scale = targetWidth / width;
26615 if(x > 0 && y == 0){
26616 scale = targetHeight / height;
26619 if(x > 0 && y > 0){
26620 scale = targetWidth / width;
26622 if(width < height){
26623 scale = targetHeight / height;
26627 context.scale(scale, scale);
26629 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26630 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26632 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26633 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26635 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26637 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26644 this.cropData = canvas.toDataURL(this.cropType);
26646 if(this.fireEvent('crop', this, this.cropData) !== false){
26647 this.process(this.file, this.cropData);
26654 setThumbBoxSize : function()
26658 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26659 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26660 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26662 this.minWidth = width;
26663 this.minHeight = height;
26665 if(this.rotate == 90 || this.rotate == 270){
26666 this.minWidth = height;
26667 this.minHeight = width;
26672 width = Math.ceil(this.minWidth * height / this.minHeight);
26674 if(this.minWidth > this.minHeight){
26676 height = Math.ceil(this.minHeight * width / this.minWidth);
26679 this.thumbEl.setStyle({
26680 width : width + 'px',
26681 height : height + 'px'
26688 setThumbBoxPosition : function()
26690 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26691 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26693 this.thumbEl.setLeft(x);
26694 this.thumbEl.setTop(y);
26698 baseRotateLevel : function()
26700 this.baseRotate = 1;
26703 typeof(this.exif) != 'undefined' &&
26704 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26705 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26707 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26710 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26714 baseScaleLevel : function()
26718 if(this.isDocument){
26720 if(this.baseRotate == 6 || this.baseRotate == 8){
26722 height = this.thumbEl.getHeight();
26723 this.baseScale = height / this.imageEl.OriginWidth;
26725 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26726 width = this.thumbEl.getWidth();
26727 this.baseScale = width / this.imageEl.OriginHeight;
26733 height = this.thumbEl.getHeight();
26734 this.baseScale = height / this.imageEl.OriginHeight;
26736 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26737 width = this.thumbEl.getWidth();
26738 this.baseScale = width / this.imageEl.OriginWidth;
26744 if(this.baseRotate == 6 || this.baseRotate == 8){
26746 width = this.thumbEl.getHeight();
26747 this.baseScale = width / this.imageEl.OriginHeight;
26749 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26750 height = this.thumbEl.getWidth();
26751 this.baseScale = height / this.imageEl.OriginHeight;
26754 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26755 height = this.thumbEl.getWidth();
26756 this.baseScale = height / this.imageEl.OriginHeight;
26758 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26759 width = this.thumbEl.getHeight();
26760 this.baseScale = width / this.imageEl.OriginWidth;
26767 width = this.thumbEl.getWidth();
26768 this.baseScale = width / this.imageEl.OriginWidth;
26770 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26771 height = this.thumbEl.getHeight();
26772 this.baseScale = height / this.imageEl.OriginHeight;
26775 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26777 height = this.thumbEl.getHeight();
26778 this.baseScale = height / this.imageEl.OriginHeight;
26780 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26781 width = this.thumbEl.getWidth();
26782 this.baseScale = width / this.imageEl.OriginWidth;
26790 getScaleLevel : function()
26792 return this.baseScale * Math.pow(1.1, this.scale);
26795 onTouchStart : function(e)
26797 if(!this.canvasLoaded){
26798 this.beforeSelectFile(e);
26802 var touches = e.browserEvent.touches;
26808 if(touches.length == 1){
26809 this.onMouseDown(e);
26813 if(touches.length != 2){
26819 for(var i = 0, finger; finger = touches[i]; i++){
26820 coords.push(finger.pageX, finger.pageY);
26823 var x = Math.pow(coords[0] - coords[2], 2);
26824 var y = Math.pow(coords[1] - coords[3], 2);
26826 this.startDistance = Math.sqrt(x + y);
26828 this.startScale = this.scale;
26830 this.pinching = true;
26831 this.dragable = false;
26835 onTouchMove : function(e)
26837 if(!this.pinching && !this.dragable){
26841 var touches = e.browserEvent.touches;
26848 this.onMouseMove(e);
26854 for(var i = 0, finger; finger = touches[i]; i++){
26855 coords.push(finger.pageX, finger.pageY);
26858 var x = Math.pow(coords[0] - coords[2], 2);
26859 var y = Math.pow(coords[1] - coords[3], 2);
26861 this.endDistance = Math.sqrt(x + y);
26863 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26865 if(!this.zoomable()){
26866 this.scale = this.startScale;
26874 onTouchEnd : function(e)
26876 this.pinching = false;
26877 this.dragable = false;
26881 process : function(file, crop)
26884 this.maskEl.mask(this.loadingText);
26887 this.xhr = new XMLHttpRequest();
26889 file.xhr = this.xhr;
26891 this.xhr.open(this.method, this.url, true);
26894 "Accept": "application/json",
26895 "Cache-Control": "no-cache",
26896 "X-Requested-With": "XMLHttpRequest"
26899 for (var headerName in headers) {
26900 var headerValue = headers[headerName];
26902 this.xhr.setRequestHeader(headerName, headerValue);
26908 this.xhr.onload = function()
26910 _this.xhrOnLoad(_this.xhr);
26913 this.xhr.onerror = function()
26915 _this.xhrOnError(_this.xhr);
26918 var formData = new FormData();
26920 formData.append('returnHTML', 'NO');
26923 formData.append('crop', crop);
26926 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26927 formData.append(this.paramName, file, file.name);
26930 if(typeof(file.filename) != 'undefined'){
26931 formData.append('filename', file.filename);
26934 if(typeof(file.mimetype) != 'undefined'){
26935 formData.append('mimetype', file.mimetype);
26938 if(this.fireEvent('arrange', this, formData) != false){
26939 this.xhr.send(formData);
26943 xhrOnLoad : function(xhr)
26946 this.maskEl.unmask();
26949 if (xhr.readyState !== 4) {
26950 this.fireEvent('exception', this, xhr);
26954 var response = Roo.decode(xhr.responseText);
26956 if(!response.success){
26957 this.fireEvent('exception', this, xhr);
26961 var response = Roo.decode(xhr.responseText);
26963 this.fireEvent('upload', this, response);
26967 xhrOnError : function()
26970 this.maskEl.unmask();
26973 Roo.log('xhr on error');
26975 var response = Roo.decode(xhr.responseText);
26981 prepare : function(file)
26984 this.maskEl.mask(this.loadingText);
26990 if(typeof(file) === 'string'){
26991 this.loadCanvas(file);
26995 if(!file || !this.urlAPI){
27000 this.cropType = file.type;
27004 if(this.fireEvent('prepare', this, this.file) != false){
27006 var reader = new FileReader();
27008 reader.onload = function (e) {
27009 if (e.target.error) {
27010 Roo.log(e.target.error);
27014 var buffer = e.target.result,
27015 dataView = new DataView(buffer),
27017 maxOffset = dataView.byteLength - 4,
27021 if (dataView.getUint16(0) === 0xffd8) {
27022 while (offset < maxOffset) {
27023 markerBytes = dataView.getUint16(offset);
27025 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27026 markerLength = dataView.getUint16(offset + 2) + 2;
27027 if (offset + markerLength > dataView.byteLength) {
27028 Roo.log('Invalid meta data: Invalid segment size.');
27032 if(markerBytes == 0xffe1){
27033 _this.parseExifData(
27040 offset += markerLength;
27050 var url = _this.urlAPI.createObjectURL(_this.file);
27052 _this.loadCanvas(url);
27057 reader.readAsArrayBuffer(this.file);
27063 parseExifData : function(dataView, offset, length)
27065 var tiffOffset = offset + 10,
27069 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27070 // No Exif data, might be XMP data instead
27074 // Check for the ASCII code for "Exif" (0x45786966):
27075 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27076 // No Exif data, might be XMP data instead
27079 if (tiffOffset + 8 > dataView.byteLength) {
27080 Roo.log('Invalid Exif data: Invalid segment size.');
27083 // Check for the two null bytes:
27084 if (dataView.getUint16(offset + 8) !== 0x0000) {
27085 Roo.log('Invalid Exif data: Missing byte alignment offset.');
27088 // Check the byte alignment:
27089 switch (dataView.getUint16(tiffOffset)) {
27091 littleEndian = true;
27094 littleEndian = false;
27097 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27100 // Check for the TIFF tag marker (0x002A):
27101 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27102 Roo.log('Invalid Exif data: Missing TIFF marker.');
27105 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27106 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27108 this.parseExifTags(
27111 tiffOffset + dirOffset,
27116 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27121 if (dirOffset + 6 > dataView.byteLength) {
27122 Roo.log('Invalid Exif data: Invalid directory offset.');
27125 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27126 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27127 if (dirEndOffset + 4 > dataView.byteLength) {
27128 Roo.log('Invalid Exif data: Invalid directory size.');
27131 for (i = 0; i < tagsNumber; i += 1) {
27135 dirOffset + 2 + 12 * i, // tag offset
27139 // Return the offset to the next directory:
27140 return dataView.getUint32(dirEndOffset, littleEndian);
27143 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
27145 var tag = dataView.getUint16(offset, littleEndian);
27147 this.exif[tag] = this.getExifValue(
27151 dataView.getUint16(offset + 2, littleEndian), // tag type
27152 dataView.getUint32(offset + 4, littleEndian), // tag length
27157 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27159 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27168 Roo.log('Invalid Exif data: Invalid tag type.');
27172 tagSize = tagType.size * length;
27173 // Determine if the value is contained in the dataOffset bytes,
27174 // or if the value at the dataOffset is a pointer to the actual data:
27175 dataOffset = tagSize > 4 ?
27176 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27177 if (dataOffset + tagSize > dataView.byteLength) {
27178 Roo.log('Invalid Exif data: Invalid data offset.');
27181 if (length === 1) {
27182 return tagType.getValue(dataView, dataOffset, littleEndian);
27185 for (i = 0; i < length; i += 1) {
27186 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27189 if (tagType.ascii) {
27191 // Concatenate the chars:
27192 for (i = 0; i < values.length; i += 1) {
27194 // Ignore the terminating NULL byte(s):
27195 if (c === '\u0000') {
27207 Roo.apply(Roo.bootstrap.UploadCropbox, {
27209 'Orientation': 0x0112
27213 1: 0, //'top-left',
27215 3: 180, //'bottom-right',
27216 // 4: 'bottom-left',
27218 6: 90, //'right-top',
27219 // 7: 'right-bottom',
27220 8: 270 //'left-bottom'
27224 // byte, 8-bit unsigned int:
27226 getValue: function (dataView, dataOffset) {
27227 return dataView.getUint8(dataOffset);
27231 // ascii, 8-bit byte:
27233 getValue: function (dataView, dataOffset) {
27234 return String.fromCharCode(dataView.getUint8(dataOffset));
27239 // short, 16 bit int:
27241 getValue: function (dataView, dataOffset, littleEndian) {
27242 return dataView.getUint16(dataOffset, littleEndian);
27246 // long, 32 bit int:
27248 getValue: function (dataView, dataOffset, littleEndian) {
27249 return dataView.getUint32(dataOffset, littleEndian);
27253 // rational = two long values, first is numerator, second is denominator:
27255 getValue: function (dataView, dataOffset, littleEndian) {
27256 return dataView.getUint32(dataOffset, littleEndian) /
27257 dataView.getUint32(dataOffset + 4, littleEndian);
27261 // slong, 32 bit signed int:
27263 getValue: function (dataView, dataOffset, littleEndian) {
27264 return dataView.getInt32(dataOffset, littleEndian);
27268 // srational, two slongs, first is numerator, second is denominator:
27270 getValue: function (dataView, dataOffset, littleEndian) {
27271 return dataView.getInt32(dataOffset, littleEndian) /
27272 dataView.getInt32(dataOffset + 4, littleEndian);
27282 cls : 'btn-group roo-upload-cropbox-rotate-left',
27283 action : 'rotate-left',
27287 cls : 'btn btn-default',
27288 html : '<i class="fa fa-undo"></i>'
27294 cls : 'btn-group roo-upload-cropbox-picture',
27295 action : 'picture',
27299 cls : 'btn btn-default',
27300 html : '<i class="fa fa-picture-o"></i>'
27306 cls : 'btn-group roo-upload-cropbox-rotate-right',
27307 action : 'rotate-right',
27311 cls : 'btn btn-default',
27312 html : '<i class="fa fa-repeat"></i>'
27320 cls : 'btn-group roo-upload-cropbox-rotate-left',
27321 action : 'rotate-left',
27325 cls : 'btn btn-default',
27326 html : '<i class="fa fa-undo"></i>'
27332 cls : 'btn-group roo-upload-cropbox-download',
27333 action : 'download',
27337 cls : 'btn btn-default',
27338 html : '<i class="fa fa-download"></i>'
27344 cls : 'btn-group roo-upload-cropbox-crop',
27349 cls : 'btn btn-default',
27350 html : '<i class="fa fa-crop"></i>'
27356 cls : 'btn-group roo-upload-cropbox-trash',
27361 cls : 'btn btn-default',
27362 html : '<i class="fa fa-trash"></i>'
27368 cls : 'btn-group roo-upload-cropbox-rotate-right',
27369 action : 'rotate-right',
27373 cls : 'btn btn-default',
27374 html : '<i class="fa fa-repeat"></i>'
27382 cls : 'btn-group roo-upload-cropbox-rotate-left',
27383 action : 'rotate-left',
27387 cls : 'btn btn-default',
27388 html : '<i class="fa fa-undo"></i>'
27394 cls : 'btn-group roo-upload-cropbox-rotate-right',
27395 action : 'rotate-right',
27399 cls : 'btn btn-default',
27400 html : '<i class="fa fa-repeat"></i>'
27413 * @class Roo.bootstrap.DocumentManager
27414 * @extends Roo.bootstrap.Component
27415 * Bootstrap DocumentManager class
27416 * @cfg {String} paramName default 'imageUpload'
27417 * @cfg {String} toolTipName default 'filename'
27418 * @cfg {String} method default POST
27419 * @cfg {String} url action url
27420 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27421 * @cfg {Boolean} multiple multiple upload default true
27422 * @cfg {Number} thumbSize default 300
27423 * @cfg {String} fieldLabel
27424 * @cfg {Number} labelWidth default 4
27425 * @cfg {String} labelAlign (left|top) default left
27426 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27429 * Create a new DocumentManager
27430 * @param {Object} config The config object
27433 Roo.bootstrap.DocumentManager = function(config){
27434 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27437 this.delegates = [];
27442 * Fire when initial the DocumentManager
27443 * @param {Roo.bootstrap.DocumentManager} this
27448 * inspect selected file
27449 * @param {Roo.bootstrap.DocumentManager} this
27450 * @param {File} file
27455 * Fire when xhr load exception
27456 * @param {Roo.bootstrap.DocumentManager} this
27457 * @param {XMLHttpRequest} xhr
27459 "exception" : true,
27461 * @event afterupload
27462 * Fire when xhr load exception
27463 * @param {Roo.bootstrap.DocumentManager} this
27464 * @param {XMLHttpRequest} xhr
27466 "afterupload" : true,
27469 * prepare the form data
27470 * @param {Roo.bootstrap.DocumentManager} this
27471 * @param {Object} formData
27476 * Fire when remove the file
27477 * @param {Roo.bootstrap.DocumentManager} this
27478 * @param {Object} file
27483 * Fire after refresh the file
27484 * @param {Roo.bootstrap.DocumentManager} this
27489 * Fire after click the image
27490 * @param {Roo.bootstrap.DocumentManager} this
27491 * @param {Object} file
27496 * Fire when upload a image and editable set to true
27497 * @param {Roo.bootstrap.DocumentManager} this
27498 * @param {Object} file
27502 * @event beforeselectfile
27503 * Fire before select file
27504 * @param {Roo.bootstrap.DocumentManager} this
27506 "beforeselectfile" : true,
27509 * Fire before process file
27510 * @param {Roo.bootstrap.DocumentManager} this
27511 * @param {Object} file
27518 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
27527 paramName : 'imageUpload',
27528 toolTipName : 'filename',
27531 labelAlign : 'left',
27536 getAutoCreate : function()
27538 var managerWidget = {
27540 cls : 'roo-document-manager',
27544 cls : 'roo-document-manager-selector',
27549 cls : 'roo-document-manager-uploader',
27553 cls : 'roo-document-manager-upload-btn',
27554 html : '<i class="fa fa-plus"></i>'
27565 cls : 'column col-md-12',
27570 if(this.fieldLabel.length){
27575 cls : 'column col-md-12',
27576 html : this.fieldLabel
27580 cls : 'column col-md-12',
27585 if(this.labelAlign == 'left'){
27589 cls : 'column col-md-' + this.labelWidth,
27590 html : this.fieldLabel
27594 cls : 'column col-md-' + (12 - this.labelWidth),
27604 cls : 'row clearfix',
27612 initEvents : function()
27614 this.managerEl = this.el.select('.roo-document-manager', true).first();
27615 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27617 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27618 this.selectorEl.hide();
27621 this.selectorEl.attr('multiple', 'multiple');
27624 this.selectorEl.on('change', this.onFileSelected, this);
27626 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27627 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27629 this.uploader.on('click', this.onUploaderClick, this);
27631 this.renderProgressDialog();
27635 window.addEventListener("resize", function() { _this.refresh(); } );
27637 this.fireEvent('initial', this);
27640 renderProgressDialog : function()
27644 this.progressDialog = new Roo.bootstrap.Modal({
27645 cls : 'roo-document-manager-progress-dialog',
27646 allow_close : false,
27656 btnclick : function() {
27657 _this.uploadCancel();
27663 this.progressDialog.render(Roo.get(document.body));
27665 this.progress = new Roo.bootstrap.Progress({
27666 cls : 'roo-document-manager-progress',
27671 this.progress.render(this.progressDialog.getChildContainer());
27673 this.progressBar = new Roo.bootstrap.ProgressBar({
27674 cls : 'roo-document-manager-progress-bar',
27677 aria_valuemax : 12,
27681 this.progressBar.render(this.progress.getChildContainer());
27684 onUploaderClick : function(e)
27686 e.preventDefault();
27688 if(this.fireEvent('beforeselectfile', this) != false){
27689 this.selectorEl.dom.click();
27694 onFileSelected : function(e)
27696 e.preventDefault();
27698 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27702 Roo.each(this.selectorEl.dom.files, function(file){
27703 if(this.fireEvent('inspect', this, file) != false){
27704 this.files.push(file);
27714 this.selectorEl.dom.value = '';
27716 if(!this.files.length){
27720 if(this.boxes > 0 && this.files.length > this.boxes){
27721 this.files = this.files.slice(0, this.boxes);
27724 this.uploader.show();
27726 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27727 this.uploader.hide();
27736 Roo.each(this.files, function(file){
27738 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27739 var f = this.renderPreview(file);
27744 if(file.type.indexOf('image') != -1){
27745 this.delegates.push(
27747 _this.process(file);
27748 }).createDelegate(this)
27756 _this.process(file);
27757 }).createDelegate(this)
27762 this.files = files;
27764 this.delegates = this.delegates.concat(docs);
27766 if(!this.delegates.length){
27771 this.progressBar.aria_valuemax = this.delegates.length;
27778 arrange : function()
27780 if(!this.delegates.length){
27781 this.progressDialog.hide();
27786 var delegate = this.delegates.shift();
27788 this.progressDialog.show();
27790 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27792 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27797 refresh : function()
27799 this.uploader.show();
27801 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27802 this.uploader.hide();
27805 Roo.isTouch ? this.closable(false) : this.closable(true);
27807 this.fireEvent('refresh', this);
27810 onRemove : function(e, el, o)
27812 e.preventDefault();
27814 this.fireEvent('remove', this, o);
27818 remove : function(o)
27822 Roo.each(this.files, function(file){
27823 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27832 this.files = files;
27839 Roo.each(this.files, function(file){
27844 file.target.remove();
27853 onClick : function(e, el, o)
27855 e.preventDefault();
27857 this.fireEvent('click', this, o);
27861 closable : function(closable)
27863 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27865 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27877 xhrOnLoad : function(xhr)
27879 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27883 if (xhr.readyState !== 4) {
27885 this.fireEvent('exception', this, xhr);
27889 var response = Roo.decode(xhr.responseText);
27891 if(!response.success){
27893 this.fireEvent('exception', this, xhr);
27897 var file = this.renderPreview(response.data);
27899 this.files.push(file);
27903 this.fireEvent('afterupload', this, xhr);
27907 xhrOnError : function(xhr)
27909 Roo.log('xhr on error');
27911 var response = Roo.decode(xhr.responseText);
27918 process : function(file)
27920 if(this.fireEvent('process', this, file) !== false){
27921 if(this.editable && file.type.indexOf('image') != -1){
27922 this.fireEvent('edit', this, file);
27926 this.uploadStart(file, false);
27933 uploadStart : function(file, crop)
27935 this.xhr = new XMLHttpRequest();
27937 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27942 file.xhr = this.xhr;
27944 this.managerEl.createChild({
27946 cls : 'roo-document-manager-loading',
27950 tooltip : file.name,
27951 cls : 'roo-document-manager-thumb',
27952 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27958 this.xhr.open(this.method, this.url, true);
27961 "Accept": "application/json",
27962 "Cache-Control": "no-cache",
27963 "X-Requested-With": "XMLHttpRequest"
27966 for (var headerName in headers) {
27967 var headerValue = headers[headerName];
27969 this.xhr.setRequestHeader(headerName, headerValue);
27975 this.xhr.onload = function()
27977 _this.xhrOnLoad(_this.xhr);
27980 this.xhr.onerror = function()
27982 _this.xhrOnError(_this.xhr);
27985 var formData = new FormData();
27987 formData.append('returnHTML', 'NO');
27990 formData.append('crop', crop);
27993 formData.append(this.paramName, file, file.name);
28000 if(this.fireEvent('prepare', this, formData, options) != false){
28002 if(options.manually){
28006 this.xhr.send(formData);
28010 this.uploadCancel();
28013 uploadCancel : function()
28019 this.delegates = [];
28021 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28028 renderPreview : function(file)
28030 if(typeof(file.target) != 'undefined' && file.target){
28034 var previewEl = this.managerEl.createChild({
28036 cls : 'roo-document-manager-preview',
28040 tooltip : file[this.toolTipName],
28041 cls : 'roo-document-manager-thumb',
28042 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28047 html : '<i class="fa fa-times-circle"></i>'
28052 var close = previewEl.select('button.close', true).first();
28054 close.on('click', this.onRemove, this, file);
28056 file.target = previewEl;
28058 var image = previewEl.select('img', true).first();
28062 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28064 image.on('click', this.onClick, this, file);
28070 onPreviewLoad : function(file, image)
28072 if(typeof(file.target) == 'undefined' || !file.target){
28076 var width = image.dom.naturalWidth || image.dom.width;
28077 var height = image.dom.naturalHeight || image.dom.height;
28079 if(width > height){
28080 file.target.addClass('wide');
28084 file.target.addClass('tall');
28089 uploadFromSource : function(file, crop)
28091 this.xhr = new XMLHttpRequest();
28093 this.managerEl.createChild({
28095 cls : 'roo-document-manager-loading',
28099 tooltip : file.name,
28100 cls : 'roo-document-manager-thumb',
28101 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28107 this.xhr.open(this.method, this.url, true);
28110 "Accept": "application/json",
28111 "Cache-Control": "no-cache",
28112 "X-Requested-With": "XMLHttpRequest"
28115 for (var headerName in headers) {
28116 var headerValue = headers[headerName];
28118 this.xhr.setRequestHeader(headerName, headerValue);
28124 this.xhr.onload = function()
28126 _this.xhrOnLoad(_this.xhr);
28129 this.xhr.onerror = function()
28131 _this.xhrOnError(_this.xhr);
28134 var formData = new FormData();
28136 formData.append('returnHTML', 'NO');
28138 formData.append('crop', crop);
28140 if(typeof(file.filename) != 'undefined'){
28141 formData.append('filename', file.filename);
28144 if(typeof(file.mimetype) != 'undefined'){
28145 formData.append('mimetype', file.mimetype);
28148 if(this.fireEvent('prepare', this, formData) != false){
28149 this.xhr.send(formData);
28159 * @class Roo.bootstrap.DocumentViewer
28160 * @extends Roo.bootstrap.Component
28161 * Bootstrap DocumentViewer class
28162 * @cfg {Boolean} showDownload (true|false) show download button (default true)
28163 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28166 * Create a new DocumentViewer
28167 * @param {Object} config The config object
28170 Roo.bootstrap.DocumentViewer = function(config){
28171 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28176 * Fire after initEvent
28177 * @param {Roo.bootstrap.DocumentViewer} this
28183 * @param {Roo.bootstrap.DocumentViewer} this
28188 * Fire after download button
28189 * @param {Roo.bootstrap.DocumentViewer} this
28194 * Fire after trash button
28195 * @param {Roo.bootstrap.DocumentViewer} this
28202 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
28204 showDownload : true,
28208 getAutoCreate : function()
28212 cls : 'roo-document-viewer',
28216 cls : 'roo-document-viewer-body',
28220 cls : 'roo-document-viewer-thumb',
28224 cls : 'roo-document-viewer-image'
28232 cls : 'roo-document-viewer-footer',
28235 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28239 cls : 'btn-group roo-document-viewer-download',
28243 cls : 'btn btn-default',
28244 html : '<i class="fa fa-download"></i>'
28250 cls : 'btn-group roo-document-viewer-trash',
28254 cls : 'btn btn-default',
28255 html : '<i class="fa fa-trash"></i>'
28268 initEvents : function()
28270 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28271 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28273 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28274 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28276 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28277 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28279 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28280 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28282 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28283 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28285 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28286 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28288 this.bodyEl.on('click', this.onClick, this);
28289 this.downloadBtn.on('click', this.onDownload, this);
28290 this.trashBtn.on('click', this.onTrash, this);
28292 this.downloadBtn.hide();
28293 this.trashBtn.hide();
28295 if(this.showDownload){
28296 this.downloadBtn.show();
28299 if(this.showTrash){
28300 this.trashBtn.show();
28303 if(!this.showDownload && !this.showTrash) {
28304 this.footerEl.hide();
28309 initial : function()
28311 this.fireEvent('initial', this);
28315 onClick : function(e)
28317 e.preventDefault();
28319 this.fireEvent('click', this);
28322 onDownload : function(e)
28324 e.preventDefault();
28326 this.fireEvent('download', this);
28329 onTrash : function(e)
28331 e.preventDefault();
28333 this.fireEvent('trash', this);
28345 * @class Roo.bootstrap.NavProgressBar
28346 * @extends Roo.bootstrap.Component
28347 * Bootstrap NavProgressBar class
28350 * Create a new nav progress bar
28351 * @param {Object} config The config object
28354 Roo.bootstrap.NavProgressBar = function(config){
28355 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28357 this.bullets = this.bullets || [];
28359 // Roo.bootstrap.NavProgressBar.register(this);
28363 * Fires when the active item changes
28364 * @param {Roo.bootstrap.NavProgressBar} this
28365 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28366 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
28373 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
28378 getAutoCreate : function()
28380 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28384 cls : 'roo-navigation-bar-group',
28388 cls : 'roo-navigation-top-bar'
28392 cls : 'roo-navigation-bullets-bar',
28396 cls : 'roo-navigation-bar'
28403 cls : 'roo-navigation-bottom-bar'
28413 initEvents: function()
28418 onRender : function(ct, position)
28420 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28422 if(this.bullets.length){
28423 Roo.each(this.bullets, function(b){
28432 addItem : function(cfg)
28434 var item = new Roo.bootstrap.NavProgressItem(cfg);
28436 item.parentId = this.id;
28437 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28440 var top = new Roo.bootstrap.Element({
28442 cls : 'roo-navigation-bar-text'
28445 var bottom = new Roo.bootstrap.Element({
28447 cls : 'roo-navigation-bar-text'
28450 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28451 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28453 var topText = new Roo.bootstrap.Element({
28455 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28458 var bottomText = new Roo.bootstrap.Element({
28460 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28463 topText.onRender(top.el, null);
28464 bottomText.onRender(bottom.el, null);
28467 item.bottomEl = bottom;
28470 this.barItems.push(item);
28475 getActive : function()
28477 var active = false;
28479 Roo.each(this.barItems, function(v){
28481 if (!v.isActive()) {
28493 setActiveItem : function(item)
28497 Roo.each(this.barItems, function(v){
28498 if (v.rid == item.rid) {
28502 if (v.isActive()) {
28503 v.setActive(false);
28508 item.setActive(true);
28510 this.fireEvent('changed', this, item, prev);
28513 getBarItem: function(rid)
28517 Roo.each(this.barItems, function(e) {
28518 if (e.rid != rid) {
28529 indexOfItem : function(item)
28533 Roo.each(this.barItems, function(v, i){
28535 if (v.rid != item.rid) {
28546 setActiveNext : function()
28548 var i = this.indexOfItem(this.getActive());
28550 if (i > this.barItems.length) {
28554 this.setActiveItem(this.barItems[i+1]);
28557 setActivePrev : function()
28559 var i = this.indexOfItem(this.getActive());
28565 this.setActiveItem(this.barItems[i-1]);
28568 format : function()
28570 if(!this.barItems.length){
28574 var width = 100 / this.barItems.length;
28576 Roo.each(this.barItems, function(i){
28577 i.el.setStyle('width', width + '%');
28578 i.topEl.el.setStyle('width', width + '%');
28579 i.bottomEl.el.setStyle('width', width + '%');
28588 * Nav Progress Item
28593 * @class Roo.bootstrap.NavProgressItem
28594 * @extends Roo.bootstrap.Component
28595 * Bootstrap NavProgressItem class
28596 * @cfg {String} rid the reference id
28597 * @cfg {Boolean} active (true|false) Is item active default false
28598 * @cfg {Boolean} disabled (true|false) Is item active default false
28599 * @cfg {String} html
28600 * @cfg {String} position (top|bottom) text position default bottom
28601 * @cfg {String} icon show icon instead of number
28604 * Create a new NavProgressItem
28605 * @param {Object} config The config object
28607 Roo.bootstrap.NavProgressItem = function(config){
28608 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28613 * The raw click event for the entire grid.
28614 * @param {Roo.bootstrap.NavProgressItem} this
28615 * @param {Roo.EventObject} e
28622 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
28628 position : 'bottom',
28631 getAutoCreate : function()
28633 var iconCls = 'roo-navigation-bar-item-icon';
28635 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28639 cls: 'roo-navigation-bar-item',
28649 cfg.cls += ' active';
28652 cfg.cls += ' disabled';
28658 disable : function()
28660 this.setDisabled(true);
28663 enable : function()
28665 this.setDisabled(false);
28668 initEvents: function()
28670 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28672 this.iconEl.on('click', this.onClick, this);
28675 onClick : function(e)
28677 e.preventDefault();
28683 if(this.fireEvent('click', this, e) === false){
28687 this.parent().setActiveItem(this);
28690 isActive: function ()
28692 return this.active;
28695 setActive : function(state)
28697 if(this.active == state){
28701 this.active = state;
28704 this.el.addClass('active');
28708 this.el.removeClass('active');
28713 setDisabled : function(state)
28715 if(this.disabled == state){
28719 this.disabled = state;
28722 this.el.addClass('disabled');
28726 this.el.removeClass('disabled');
28729 tooltipEl : function()
28731 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28744 * @class Roo.bootstrap.FieldLabel
28745 * @extends Roo.bootstrap.Component
28746 * Bootstrap FieldLabel class
28747 * @cfg {String} html contents of the element
28748 * @cfg {String} tag tag of the element default label
28749 * @cfg {String} cls class of the element
28750 * @cfg {String} target label target
28751 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28752 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28753 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28754 * @cfg {String} iconTooltip default "This field is required"
28757 * Create a new FieldLabel
28758 * @param {Object} config The config object
28761 Roo.bootstrap.FieldLabel = function(config){
28762 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28767 * Fires after the field has been marked as invalid.
28768 * @param {Roo.form.FieldLabel} this
28769 * @param {String} msg The validation message
28774 * Fires after the field has been validated with no errors.
28775 * @param {Roo.form.FieldLabel} this
28781 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28788 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28789 validClass : 'text-success fa fa-lg fa-check',
28790 iconTooltip : 'This field is required',
28792 getAutoCreate : function(){
28796 cls : 'roo-bootstrap-field-label ' + this.cls,
28802 tooltip : this.iconTooltip
28814 initEvents: function()
28816 Roo.bootstrap.Element.superclass.initEvents.call(this);
28818 this.iconEl = this.el.select('i', true).first();
28820 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28822 Roo.bootstrap.FieldLabel.register(this);
28826 * Mark this field as valid
28828 markValid : function()
28830 this.iconEl.show();
28832 this.iconEl.removeClass(this.invalidClass);
28834 this.iconEl.addClass(this.validClass);
28836 this.fireEvent('valid', this);
28840 * Mark this field as invalid
28841 * @param {String} msg The validation message
28843 markInvalid : function(msg)
28845 this.iconEl.show();
28847 this.iconEl.removeClass(this.validClass);
28849 this.iconEl.addClass(this.invalidClass);
28851 this.fireEvent('invalid', this, msg);
28857 Roo.apply(Roo.bootstrap.FieldLabel, {
28862 * register a FieldLabel Group
28863 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28865 register : function(label)
28867 if(this.groups.hasOwnProperty(label.target)){
28871 this.groups[label.target] = label;
28875 * fetch a FieldLabel Group based on the target
28876 * @param {string} target
28877 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28879 get: function(target) {
28880 if (typeof(this.groups[target]) == 'undefined') {
28884 return this.groups[target] ;
28893 * page DateSplitField.
28899 * @class Roo.bootstrap.DateSplitField
28900 * @extends Roo.bootstrap.Component
28901 * Bootstrap DateSplitField class
28902 * @cfg {string} fieldLabel - the label associated
28903 * @cfg {Number} labelWidth set the width of label (0-12)
28904 * @cfg {String} labelAlign (top|left)
28905 * @cfg {Boolean} dayAllowBlank (true|false) default false
28906 * @cfg {Boolean} monthAllowBlank (true|false) default false
28907 * @cfg {Boolean} yearAllowBlank (true|false) default false
28908 * @cfg {string} dayPlaceholder
28909 * @cfg {string} monthPlaceholder
28910 * @cfg {string} yearPlaceholder
28911 * @cfg {string} dayFormat default 'd'
28912 * @cfg {string} monthFormat default 'm'
28913 * @cfg {string} yearFormat default 'Y'
28917 * Create a new DateSplitField
28918 * @param {Object} config The config object
28921 Roo.bootstrap.DateSplitField = function(config){
28922 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28928 * getting the data of years
28929 * @param {Roo.bootstrap.DateSplitField} this
28930 * @param {Object} years
28935 * getting the data of days
28936 * @param {Roo.bootstrap.DateSplitField} this
28937 * @param {Object} days
28942 * Fires after the field has been marked as invalid.
28943 * @param {Roo.form.Field} this
28944 * @param {String} msg The validation message
28949 * Fires after the field has been validated with no errors.
28950 * @param {Roo.form.Field} this
28956 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28959 labelAlign : 'top',
28961 dayAllowBlank : false,
28962 monthAllowBlank : false,
28963 yearAllowBlank : false,
28964 dayPlaceholder : '',
28965 monthPlaceholder : '',
28966 yearPlaceholder : '',
28970 isFormField : true,
28972 getAutoCreate : function()
28976 cls : 'row roo-date-split-field-group',
28981 cls : 'form-hidden-field roo-date-split-field-group-value',
28987 if(this.fieldLabel){
28990 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28994 html : this.fieldLabel
29000 Roo.each(['day', 'month', 'year'], function(t){
29003 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
29010 inputEl: function ()
29012 return this.el.select('.roo-date-split-field-group-value', true).first();
29015 onRender : function(ct, position)
29019 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29021 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29023 this.dayField = new Roo.bootstrap.ComboBox({
29024 allowBlank : this.dayAllowBlank,
29025 alwaysQuery : true,
29026 displayField : 'value',
29029 forceSelection : true,
29031 placeholder : this.dayPlaceholder,
29032 selectOnFocus : true,
29033 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29034 triggerAction : 'all',
29036 valueField : 'value',
29037 store : new Roo.data.SimpleStore({
29038 data : (function() {
29040 _this.fireEvent('days', _this, days);
29043 fields : [ 'value' ]
29046 select : function (_self, record, index)
29048 _this.setValue(_this.getValue());
29053 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29055 this.monthField = new Roo.bootstrap.MonthField({
29056 after : '<i class=\"fa fa-calendar\"></i>',
29057 allowBlank : this.monthAllowBlank,
29058 placeholder : this.monthPlaceholder,
29061 render : function (_self)
29063 this.el.select('span.input-group-addon', true).first().on('click', function(e){
29064 e.preventDefault();
29068 select : function (_self, oldvalue, newvalue)
29070 _this.setValue(_this.getValue());
29075 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29077 this.yearField = new Roo.bootstrap.ComboBox({
29078 allowBlank : this.yearAllowBlank,
29079 alwaysQuery : true,
29080 displayField : 'value',
29083 forceSelection : true,
29085 placeholder : this.yearPlaceholder,
29086 selectOnFocus : true,
29087 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29088 triggerAction : 'all',
29090 valueField : 'value',
29091 store : new Roo.data.SimpleStore({
29092 data : (function() {
29094 _this.fireEvent('years', _this, years);
29097 fields : [ 'value' ]
29100 select : function (_self, record, index)
29102 _this.setValue(_this.getValue());
29107 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29110 setValue : function(v, format)
29112 this.inputEl.dom.value = v;
29114 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29116 var d = Date.parseDate(v, f);
29123 this.setDay(d.format(this.dayFormat));
29124 this.setMonth(d.format(this.monthFormat));
29125 this.setYear(d.format(this.yearFormat));
29132 setDay : function(v)
29134 this.dayField.setValue(v);
29135 this.inputEl.dom.value = this.getValue();
29140 setMonth : function(v)
29142 this.monthField.setValue(v, true);
29143 this.inputEl.dom.value = this.getValue();
29148 setYear : function(v)
29150 this.yearField.setValue(v);
29151 this.inputEl.dom.value = this.getValue();
29156 getDay : function()
29158 return this.dayField.getValue();
29161 getMonth : function()
29163 return this.monthField.getValue();
29166 getYear : function()
29168 return this.yearField.getValue();
29171 getValue : function()
29173 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29175 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29185 this.inputEl.dom.value = '';
29190 validate : function()
29192 var d = this.dayField.validate();
29193 var m = this.monthField.validate();
29194 var y = this.yearField.validate();
29199 (!this.dayAllowBlank && !d) ||
29200 (!this.monthAllowBlank && !m) ||
29201 (!this.yearAllowBlank && !y)
29206 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29215 this.markInvalid();
29220 markValid : function()
29223 var label = this.el.select('label', true).first();
29224 var icon = this.el.select('i.fa-star', true).first();
29230 this.fireEvent('valid', this);
29234 * Mark this field as invalid
29235 * @param {String} msg The validation message
29237 markInvalid : function(msg)
29240 var label = this.el.select('label', true).first();
29241 var icon = this.el.select('i.fa-star', true).first();
29243 if(label && !icon){
29244 this.el.select('.roo-date-split-field-label', true).createChild({
29246 cls : 'text-danger fa fa-lg fa-star',
29247 tooltip : 'This field is required',
29248 style : 'margin-right:5px;'
29252 this.fireEvent('invalid', this, msg);
29255 clearInvalid : function()
29257 var label = this.el.select('label', true).first();
29258 var icon = this.el.select('i.fa-star', true).first();
29264 this.fireEvent('valid', this);
29267 getName: function()
29277 * http://masonry.desandro.com
29279 * The idea is to render all the bricks based on vertical width...
29281 * The original code extends 'outlayer' - we might need to use that....
29287 * @class Roo.bootstrap.LayoutMasonry
29288 * @extends Roo.bootstrap.Component
29289 * Bootstrap Layout Masonry class
29292 * Create a new Element
29293 * @param {Object} config The config object
29296 Roo.bootstrap.LayoutMasonry = function(config){
29297 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29303 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
29306 * @cfg {Boolean} isLayoutInstant = no animation?
29308 isLayoutInstant : false, // needed?
29311 * @cfg {Number} boxWidth width of the columns
29316 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
29321 * @cfg {Number} padWidth padding below box..
29326 * @cfg {Number} gutter gutter width..
29331 * @cfg {Number} maxCols maximum number of columns
29337 * @cfg {Boolean} isAutoInitial defalut true
29339 isAutoInitial : true,
29344 * @cfg {Boolean} isHorizontal defalut false
29346 isHorizontal : false,
29348 currentSize : null,
29354 bricks: null, //CompositeElement
29358 _isLayoutInited : false,
29360 // isAlternative : false, // only use for vertical layout...
29363 * @cfg {Number} alternativePadWidth padding below box..
29365 alternativePadWidth : 50,
29367 getAutoCreate : function(){
29371 cls: 'blog-masonary-wrapper ' + this.cls,
29373 cls : 'mas-boxes masonary'
29380 getChildContainer: function( )
29382 if (this.boxesEl) {
29383 return this.boxesEl;
29386 this.boxesEl = this.el.select('.mas-boxes').first();
29388 return this.boxesEl;
29392 initEvents : function()
29396 if(this.isAutoInitial){
29397 Roo.log('hook children rendered');
29398 this.on('childrenrendered', function() {
29399 Roo.log('children rendered');
29405 initial : function()
29407 this.currentSize = this.el.getBox(true);
29409 Roo.EventManager.onWindowResize(this.resize, this);
29411 if(!this.isAutoInitial){
29419 //this.layout.defer(500,this);
29423 resize : function()
29427 var cs = this.el.getBox(true);
29429 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29430 Roo.log("no change in with or X");
29434 this.currentSize = cs;
29440 layout : function()
29442 this._resetLayout();
29444 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29446 this.layoutItems( isInstant );
29448 this._isLayoutInited = true;
29452 _resetLayout : function()
29454 if(this.isHorizontal){
29455 this.horizontalMeasureColumns();
29459 this.verticalMeasureColumns();
29463 verticalMeasureColumns : function()
29465 this.getContainerWidth();
29467 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29468 // this.colWidth = Math.floor(this.containerWidth * 0.8);
29472 var boxWidth = this.boxWidth + this.padWidth;
29474 if(this.containerWidth < this.boxWidth){
29475 boxWidth = this.containerWidth
29478 var containerWidth = this.containerWidth;
29480 var cols = Math.floor(containerWidth / boxWidth);
29482 this.cols = Math.max( cols, 1 );
29484 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29486 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29488 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29490 this.colWidth = boxWidth + avail - this.padWidth;
29492 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29493 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
29496 horizontalMeasureColumns : function()
29498 this.getContainerWidth();
29500 var boxWidth = this.boxWidth;
29502 if(this.containerWidth < boxWidth){
29503 boxWidth = this.containerWidth;
29506 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29508 this.el.setHeight(boxWidth);
29512 getContainerWidth : function()
29514 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
29517 layoutItems : function( isInstant )
29519 var items = Roo.apply([], this.bricks);
29521 if(this.isHorizontal){
29522 this._horizontalLayoutItems( items , isInstant );
29526 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29527 // this._verticalAlternativeLayoutItems( items , isInstant );
29531 this._verticalLayoutItems( items , isInstant );
29535 _verticalLayoutItems : function ( items , isInstant)
29537 if ( !items || !items.length ) {
29542 ['xs', 'xs', 'xs', 'tall'],
29543 ['xs', 'xs', 'tall'],
29544 ['xs', 'xs', 'sm'],
29545 ['xs', 'xs', 'xs'],
29551 ['sm', 'xs', 'xs'],
29555 ['tall', 'xs', 'xs', 'xs'],
29556 ['tall', 'xs', 'xs'],
29568 Roo.each(items, function(item, k){
29570 switch (item.size) {
29571 // these layouts take up a full box,
29582 boxes.push([item]);
29605 var filterPattern = function(box, length)
29613 var pattern = box.slice(0, length);
29617 Roo.each(pattern, function(i){
29618 format.push(i.size);
29621 Roo.each(standard, function(s){
29623 if(String(s) != String(format)){
29632 if(!match && length == 1){
29637 filterPattern(box, length - 1);
29641 queue.push(pattern);
29643 box = box.slice(length, box.length);
29645 filterPattern(box, 4);
29651 Roo.each(boxes, function(box, k){
29657 if(box.length == 1){
29662 filterPattern(box, 4);
29666 this._processVerticalLayoutQueue( queue, isInstant );
29670 // _verticalAlternativeLayoutItems : function( items , isInstant )
29672 // if ( !items || !items.length ) {
29676 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
29680 _horizontalLayoutItems : function ( items , isInstant)
29682 if ( !items || !items.length || items.length < 3) {
29688 var eItems = items.slice(0, 3);
29690 items = items.slice(3, items.length);
29693 ['xs', 'xs', 'xs', 'wide'],
29694 ['xs', 'xs', 'wide'],
29695 ['xs', 'xs', 'sm'],
29696 ['xs', 'xs', 'xs'],
29702 ['sm', 'xs', 'xs'],
29706 ['wide', 'xs', 'xs', 'xs'],
29707 ['wide', 'xs', 'xs'],
29720 Roo.each(items, function(item, k){
29722 switch (item.size) {
29733 boxes.push([item]);
29757 var filterPattern = function(box, length)
29765 var pattern = box.slice(0, length);
29769 Roo.each(pattern, function(i){
29770 format.push(i.size);
29773 Roo.each(standard, function(s){
29775 if(String(s) != String(format)){
29784 if(!match && length == 1){
29789 filterPattern(box, length - 1);
29793 queue.push(pattern);
29795 box = box.slice(length, box.length);
29797 filterPattern(box, 4);
29803 Roo.each(boxes, function(box, k){
29809 if(box.length == 1){
29814 filterPattern(box, 4);
29821 var pos = this.el.getBox(true);
29825 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29827 var hit_end = false;
29829 Roo.each(queue, function(box){
29833 Roo.each(box, function(b){
29835 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29845 Roo.each(box, function(b){
29847 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29850 mx = Math.max(mx, b.x);
29854 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29858 Roo.each(box, function(b){
29860 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29874 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29877 /** Sets position of item in DOM
29878 * @param {Element} item
29879 * @param {Number} x - horizontal position
29880 * @param {Number} y - vertical position
29881 * @param {Boolean} isInstant - disables transitions
29883 _processVerticalLayoutQueue : function( queue, isInstant )
29885 var pos = this.el.getBox(true);
29890 for (var i = 0; i < this.cols; i++){
29894 Roo.each(queue, function(box, k){
29896 var col = k % this.cols;
29898 Roo.each(box, function(b,kk){
29900 b.el.position('absolute');
29902 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29903 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29905 if(b.size == 'md-left' || b.size == 'md-right'){
29906 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29907 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29910 b.el.setWidth(width);
29911 b.el.setHeight(height);
29913 b.el.select('iframe',true).setSize(width,height);
29917 for (var i = 0; i < this.cols; i++){
29919 if(maxY[i] < maxY[col]){
29924 col = Math.min(col, i);
29928 x = pos.x + col * (this.colWidth + this.padWidth);
29932 var positions = [];
29934 switch (box.length){
29936 positions = this.getVerticalOneBoxColPositions(x, y, box);
29939 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29942 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29945 positions = this.getVerticalFourBoxColPositions(x, y, box);
29951 Roo.each(box, function(b,kk){
29953 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29955 var sz = b.el.getSize();
29957 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29965 for (var i = 0; i < this.cols; i++){
29966 mY = Math.max(mY, maxY[i]);
29969 this.el.setHeight(mY - pos.y);
29973 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29975 // var pos = this.el.getBox(true);
29978 // var maxX = pos.right;
29980 // var maxHeight = 0;
29982 // Roo.each(items, function(item, k){
29986 // item.el.position('absolute');
29988 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29990 // item.el.setWidth(width);
29992 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29994 // item.el.setHeight(height);
29997 // item.el.setXY([x, y], isInstant ? false : true);
29999 // item.el.setXY([maxX - width, y], isInstant ? false : true);
30002 // y = y + height + this.alternativePadWidth;
30004 // maxHeight = maxHeight + height + this.alternativePadWidth;
30008 // this.el.setHeight(maxHeight);
30012 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30014 var pos = this.el.getBox(true);
30019 var maxX = pos.right;
30021 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30023 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30025 Roo.each(queue, function(box, k){
30027 Roo.each(box, function(b, kk){
30029 b.el.position('absolute');
30031 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30032 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30034 if(b.size == 'md-left' || b.size == 'md-right'){
30035 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30036 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30039 b.el.setWidth(width);
30040 b.el.setHeight(height);
30048 var positions = [];
30050 switch (box.length){
30052 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30055 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30058 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30061 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30067 Roo.each(box, function(b,kk){
30069 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30071 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30079 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30081 Roo.each(eItems, function(b,k){
30083 b.size = (k == 0) ? 'sm' : 'xs';
30084 b.x = (k == 0) ? 2 : 1;
30085 b.y = (k == 0) ? 2 : 1;
30087 b.el.position('absolute');
30089 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30091 b.el.setWidth(width);
30093 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30095 b.el.setHeight(height);
30099 var positions = [];
30102 x : maxX - this.unitWidth * 2 - this.gutter,
30107 x : maxX - this.unitWidth,
30108 y : minY + (this.unitWidth + this.gutter) * 2
30112 x : maxX - this.unitWidth * 3 - this.gutter * 2,
30116 Roo.each(eItems, function(b,k){
30118 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30124 getVerticalOneBoxColPositions : function(x, y, box)
30128 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30130 if(box[0].size == 'md-left'){
30134 if(box[0].size == 'md-right'){
30139 x : x + (this.unitWidth + this.gutter) * rand,
30146 getVerticalTwoBoxColPositions : function(x, y, box)
30150 if(box[0].size == 'xs'){
30154 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30158 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30172 x : x + (this.unitWidth + this.gutter) * 2,
30173 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30180 getVerticalThreeBoxColPositions : function(x, y, box)
30184 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30192 x : x + (this.unitWidth + this.gutter) * 1,
30197 x : x + (this.unitWidth + this.gutter) * 2,
30205 if(box[0].size == 'xs' && box[1].size == 'xs'){
30214 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30218 x : x + (this.unitWidth + this.gutter) * 1,
30232 x : x + (this.unitWidth + this.gutter) * 2,
30237 x : x + (this.unitWidth + this.gutter) * 2,
30238 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30245 getVerticalFourBoxColPositions : function(x, y, box)
30249 if(box[0].size == 'xs'){
30258 y : y + (this.unitHeight + this.gutter) * 1
30263 y : y + (this.unitHeight + this.gutter) * 2
30267 x : x + (this.unitWidth + this.gutter) * 1,
30281 x : x + (this.unitWidth + this.gutter) * 2,
30286 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30287 y : y + (this.unitHeight + this.gutter) * 1
30291 x : x + (this.unitWidth + this.gutter) * 2,
30292 y : y + (this.unitWidth + this.gutter) * 2
30299 getHorizontalOneBoxColPositions : function(maxX, minY, box)
30303 if(box[0].size == 'md-left'){
30305 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30312 if(box[0].size == 'md-right'){
30314 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30315 y : minY + (this.unitWidth + this.gutter) * 1
30321 var rand = Math.floor(Math.random() * (4 - box[0].y));
30324 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30325 y : minY + (this.unitWidth + this.gutter) * rand
30332 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30336 if(box[0].size == 'xs'){
30339 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30344 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30345 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30353 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30358 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30359 y : minY + (this.unitWidth + this.gutter) * 2
30366 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30370 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30373 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30378 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30379 y : minY + (this.unitWidth + this.gutter) * 1
30383 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30384 y : minY + (this.unitWidth + this.gutter) * 2
30391 if(box[0].size == 'xs' && box[1].size == 'xs'){
30394 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30399 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30404 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30405 y : minY + (this.unitWidth + this.gutter) * 1
30413 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30418 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30419 y : minY + (this.unitWidth + this.gutter) * 2
30423 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30424 y : minY + (this.unitWidth + this.gutter) * 2
30431 getHorizontalFourBoxColPositions : function(maxX, minY, box)
30435 if(box[0].size == 'xs'){
30438 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30443 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30448 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),
30453 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30454 y : minY + (this.unitWidth + this.gutter) * 1
30462 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30467 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30468 y : minY + (this.unitWidth + this.gutter) * 2
30472 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30473 y : minY + (this.unitWidth + this.gutter) * 2
30477 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),
30478 y : minY + (this.unitWidth + this.gutter) * 2
30492 * http://masonry.desandro.com
30494 * The idea is to render all the bricks based on vertical width...
30496 * The original code extends 'outlayer' - we might need to use that....
30502 * @class Roo.bootstrap.LayoutMasonryAuto
30503 * @extends Roo.bootstrap.Component
30504 * Bootstrap Layout Masonry class
30507 * Create a new Element
30508 * @param {Object} config The config object
30511 Roo.bootstrap.LayoutMasonryAuto = function(config){
30512 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30515 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
30518 * @cfg {Boolean} isFitWidth - resize the width..
30520 isFitWidth : false, // options..
30522 * @cfg {Boolean} isOriginLeft = left align?
30524 isOriginLeft : true,
30526 * @cfg {Boolean} isOriginTop = top align?
30528 isOriginTop : false,
30530 * @cfg {Boolean} isLayoutInstant = no animation?
30532 isLayoutInstant : false, // needed?
30534 * @cfg {Boolean} isResizingContainer = not sure if this is used..
30536 isResizingContainer : true,
30538 * @cfg {Number} columnWidth width of the columns
30544 * @cfg {Number} maxCols maximum number of columns
30549 * @cfg {Number} padHeight padding below box..
30555 * @cfg {Boolean} isAutoInitial defalut true
30558 isAutoInitial : true,
30564 initialColumnWidth : 0,
30565 currentSize : null,
30567 colYs : null, // array.
30574 bricks: null, //CompositeElement
30575 cols : 0, // array?
30576 // element : null, // wrapped now this.el
30577 _isLayoutInited : null,
30580 getAutoCreate : function(){
30584 cls: 'blog-masonary-wrapper ' + this.cls,
30586 cls : 'mas-boxes masonary'
30593 getChildContainer: function( )
30595 if (this.boxesEl) {
30596 return this.boxesEl;
30599 this.boxesEl = this.el.select('.mas-boxes').first();
30601 return this.boxesEl;
30605 initEvents : function()
30609 if(this.isAutoInitial){
30610 Roo.log('hook children rendered');
30611 this.on('childrenrendered', function() {
30612 Roo.log('children rendered');
30619 initial : function()
30621 this.reloadItems();
30623 this.currentSize = this.el.getBox(true);
30625 /// was window resize... - let's see if this works..
30626 Roo.EventManager.onWindowResize(this.resize, this);
30628 if(!this.isAutoInitial){
30633 this.layout.defer(500,this);
30636 reloadItems: function()
30638 this.bricks = this.el.select('.masonry-brick', true);
30640 this.bricks.each(function(b) {
30641 //Roo.log(b.getSize());
30642 if (!b.attr('originalwidth')) {
30643 b.attr('originalwidth', b.getSize().width);
30648 Roo.log(this.bricks.elements.length);
30651 resize : function()
30654 var cs = this.el.getBox(true);
30656 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30657 Roo.log("no change in with or X");
30660 this.currentSize = cs;
30664 layout : function()
30667 this._resetLayout();
30668 //this._manageStamps();
30670 // don't animate first layout
30671 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30672 this.layoutItems( isInstant );
30674 // flag for initalized
30675 this._isLayoutInited = true;
30678 layoutItems : function( isInstant )
30680 //var items = this._getItemsForLayout( this.items );
30681 // original code supports filtering layout items.. we just ignore it..
30683 this._layoutItems( this.bricks , isInstant );
30685 this._postLayout();
30687 _layoutItems : function ( items , isInstant)
30689 //this.fireEvent( 'layout', this, items );
30692 if ( !items || !items.elements.length ) {
30693 // no items, emit event with empty array
30698 items.each(function(item) {
30699 Roo.log("layout item");
30701 // get x/y object from method
30702 var position = this._getItemLayoutPosition( item );
30704 position.item = item;
30705 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30706 queue.push( position );
30709 this._processLayoutQueue( queue );
30711 /** Sets position of item in DOM
30712 * @param {Element} item
30713 * @param {Number} x - horizontal position
30714 * @param {Number} y - vertical position
30715 * @param {Boolean} isInstant - disables transitions
30717 _processLayoutQueue : function( queue )
30719 for ( var i=0, len = queue.length; i < len; i++ ) {
30720 var obj = queue[i];
30721 obj.item.position('absolute');
30722 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30728 * Any logic you want to do after each layout,
30729 * i.e. size the container
30731 _postLayout : function()
30733 this.resizeContainer();
30736 resizeContainer : function()
30738 if ( !this.isResizingContainer ) {
30741 var size = this._getContainerSize();
30743 this.el.setSize(size.width,size.height);
30744 this.boxesEl.setSize(size.width,size.height);
30750 _resetLayout : function()
30752 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30753 this.colWidth = this.el.getWidth();
30754 //this.gutter = this.el.getWidth();
30756 this.measureColumns();
30762 this.colYs.push( 0 );
30768 measureColumns : function()
30770 this.getContainerWidth();
30771 // if columnWidth is 0, default to outerWidth of first item
30772 if ( !this.columnWidth ) {
30773 var firstItem = this.bricks.first();
30774 Roo.log(firstItem);
30775 this.columnWidth = this.containerWidth;
30776 if (firstItem && firstItem.attr('originalwidth') ) {
30777 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30779 // columnWidth fall back to item of first element
30780 Roo.log("set column width?");
30781 this.initialColumnWidth = this.columnWidth ;
30783 // if first elem has no width, default to size of container
30788 if (this.initialColumnWidth) {
30789 this.columnWidth = this.initialColumnWidth;
30794 // column width is fixed at the top - however if container width get's smaller we should
30797 // this bit calcs how man columns..
30799 var columnWidth = this.columnWidth += this.gutter;
30801 // calculate columns
30802 var containerWidth = this.containerWidth + this.gutter;
30804 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30805 // fix rounding errors, typically with gutters
30806 var excess = columnWidth - containerWidth % columnWidth;
30809 // if overshoot is less than a pixel, round up, otherwise floor it
30810 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30811 cols = Math[ mathMethod ]( cols );
30812 this.cols = Math.max( cols, 1 );
30813 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30815 // padding positioning..
30816 var totalColWidth = this.cols * this.columnWidth;
30817 var padavail = this.containerWidth - totalColWidth;
30818 // so for 2 columns - we need 3 'pads'
30820 var padNeeded = (1+this.cols) * this.padWidth;
30822 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30824 this.columnWidth += padExtra
30825 //this.padWidth = Math.floor(padavail / ( this.cols));
30827 // adjust colum width so that padding is fixed??
30829 // we have 3 columns ... total = width * 3
30830 // we have X left over... that should be used by
30832 //if (this.expandC) {
30840 getContainerWidth : function()
30842 /* // container is parent if fit width
30843 var container = this.isFitWidth ? this.element.parentNode : this.element;
30844 // check that this.size and size are there
30845 // IE8 triggers resize on body size change, so they might not be
30847 var size = getSize( container ); //FIXME
30848 this.containerWidth = size && size.innerWidth; //FIXME
30851 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30855 _getItemLayoutPosition : function( item ) // what is item?
30857 // we resize the item to our columnWidth..
30859 item.setWidth(this.columnWidth);
30860 item.autoBoxAdjust = false;
30862 var sz = item.getSize();
30864 // how many columns does this brick span
30865 var remainder = this.containerWidth % this.columnWidth;
30867 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30868 // round if off by 1 pixel, otherwise use ceil
30869 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30870 colSpan = Math.min( colSpan, this.cols );
30872 // normally this should be '1' as we dont' currently allow multi width columns..
30874 var colGroup = this._getColGroup( colSpan );
30875 // get the minimum Y value from the columns
30876 var minimumY = Math.min.apply( Math, colGroup );
30877 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30879 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30881 // position the brick
30883 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30884 y: this.currentSize.y + minimumY + this.padHeight
30888 // apply setHeight to necessary columns
30889 var setHeight = minimumY + sz.height + this.padHeight;
30890 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30892 var setSpan = this.cols + 1 - colGroup.length;
30893 for ( var i = 0; i < setSpan; i++ ) {
30894 this.colYs[ shortColIndex + i ] = setHeight ;
30901 * @param {Number} colSpan - number of columns the element spans
30902 * @returns {Array} colGroup
30904 _getColGroup : function( colSpan )
30906 if ( colSpan < 2 ) {
30907 // if brick spans only one column, use all the column Ys
30912 // how many different places could this brick fit horizontally
30913 var groupCount = this.cols + 1 - colSpan;
30914 // for each group potential horizontal position
30915 for ( var i = 0; i < groupCount; i++ ) {
30916 // make an array of colY values for that one group
30917 var groupColYs = this.colYs.slice( i, i + colSpan );
30918 // and get the max value of the array
30919 colGroup[i] = Math.max.apply( Math, groupColYs );
30924 _manageStamp : function( stamp )
30926 var stampSize = stamp.getSize();
30927 var offset = stamp.getBox();
30928 // get the columns that this stamp affects
30929 var firstX = this.isOriginLeft ? offset.x : offset.right;
30930 var lastX = firstX + stampSize.width;
30931 var firstCol = Math.floor( firstX / this.columnWidth );
30932 firstCol = Math.max( 0, firstCol );
30934 var lastCol = Math.floor( lastX / this.columnWidth );
30935 // lastCol should not go over if multiple of columnWidth #425
30936 lastCol -= lastX % this.columnWidth ? 0 : 1;
30937 lastCol = Math.min( this.cols - 1, lastCol );
30939 // set colYs to bottom of the stamp
30940 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30943 for ( var i = firstCol; i <= lastCol; i++ ) {
30944 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30949 _getContainerSize : function()
30951 this.maxY = Math.max.apply( Math, this.colYs );
30956 if ( this.isFitWidth ) {
30957 size.width = this._getContainerFitWidth();
30963 _getContainerFitWidth : function()
30965 var unusedCols = 0;
30966 // count unused columns
30969 if ( this.colYs[i] !== 0 ) {
30974 // fit container to columns that have been used
30975 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30978 needsResizeLayout : function()
30980 var previousWidth = this.containerWidth;
30981 this.getContainerWidth();
30982 return previousWidth !== this.containerWidth;
30997 * @class Roo.bootstrap.MasonryBrick
30998 * @extends Roo.bootstrap.Component
30999 * Bootstrap MasonryBrick class
31002 * Create a new MasonryBrick
31003 * @param {Object} config The config object
31006 Roo.bootstrap.MasonryBrick = function(config){
31007 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31013 * When a MasonryBrick is clcik
31014 * @param {Roo.bootstrap.MasonryBrick} this
31015 * @param {Roo.EventObject} e
31021 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
31024 * @cfg {String} title
31028 * @cfg {String} html
31032 * @cfg {String} bgimage
31036 * @cfg {String} videourl
31040 * @cfg {String} cls
31044 * @cfg {String} href
31048 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31053 * @cfg {String} (center|bottom) placetitle
31058 * @cfg {Boolean} isFitContainer defalut true
31060 isFitContainer : true,
31063 * @cfg {Boolean} preventDefault defalut false
31065 preventDefault : false,
31067 getAutoCreate : function()
31069 if(!this.isFitContainer){
31070 return this.getSplitAutoCreate();
31073 var cls = 'masonry-brick masonry-brick-full';
31075 if(this.href.length){
31076 cls += ' masonry-brick-link';
31079 if(this.bgimage.length){
31080 cls += ' masonry-brick-image';
31083 if(!this.html.length){
31084 cls += ' enable-mask';
31088 cls += ' masonry-' + this.size + '-brick';
31091 if(this.placetitle.length){
31093 switch (this.placetitle) {
31095 cls += ' masonry-center-title';
31098 cls += ' masonry-bottom-title';
31105 if(!this.html.length && !this.bgimage.length){
31106 cls += ' masonry-center-title';
31109 if(!this.html.length && this.bgimage.length){
31110 cls += ' masonry-bottom-title';
31115 cls += ' ' + this.cls;
31119 tag: (this.href.length) ? 'a' : 'div',
31124 cls: 'masonry-brick-paragraph',
31130 if(this.href.length){
31131 cfg.href = this.href;
31134 var cn = cfg.cn[0].cn;
31136 if(this.title.length){
31139 cls: 'masonry-brick-title',
31144 if(this.html.length){
31147 cls: 'masonry-brick-text',
31151 if (!this.title.length && !this.html.length) {
31152 cfg.cn[0].cls += ' hide';
31155 if(this.bgimage.length){
31158 cls: 'masonry-brick-image-view',
31163 if(this.videourl.length){
31164 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31165 // youtube support only?
31168 cls: 'masonry-brick-image-view',
31171 allowfullscreen : true
31179 cls: 'masonry-brick-mask'
31186 getSplitAutoCreate : function()
31188 var cls = 'masonry-brick masonry-brick-split';
31190 if(this.href.length){
31191 cls += ' masonry-brick-link';
31194 if(this.bgimage.length){
31195 cls += ' masonry-brick-image';
31199 cls += ' masonry-' + this.size + '-brick';
31202 switch (this.placetitle) {
31204 cls += ' masonry-center-title';
31207 cls += ' masonry-bottom-title';
31210 if(!this.bgimage.length){
31211 cls += ' masonry-center-title';
31214 if(this.bgimage.length){
31215 cls += ' masonry-bottom-title';
31221 cls += ' ' + this.cls;
31225 tag: (this.href.length) ? 'a' : 'div',
31230 cls: 'masonry-brick-split-head',
31234 cls: 'masonry-brick-paragraph',
31241 cls: 'masonry-brick-split-body',
31247 if(this.href.length){
31248 cfg.href = this.href;
31251 if(this.title.length){
31252 cfg.cn[0].cn[0].cn.push({
31254 cls: 'masonry-brick-title',
31259 if(this.html.length){
31260 cfg.cn[1].cn.push({
31262 cls: 'masonry-brick-text',
31267 if(this.bgimage.length){
31268 cfg.cn[0].cn.push({
31270 cls: 'masonry-brick-image-view',
31275 if(this.videourl.length){
31276 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31277 // youtube support only?
31278 cfg.cn[0].cn.cn.push({
31280 cls: 'masonry-brick-image-view',
31283 allowfullscreen : true
31290 initEvents: function()
31292 switch (this.size) {
31325 this.el.on('touchstart', this.onTouchStart, this);
31326 this.el.on('touchmove', this.onTouchMove, this);
31327 this.el.on('touchend', this.onTouchEnd, this);
31328 this.el.on('contextmenu', this.onContextMenu, this);
31330 this.el.on('mouseenter' ,this.enter, this);
31331 this.el.on('mouseleave', this.leave, this);
31332 this.el.on('click', this.onClick, this);
31335 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31336 this.parent().bricks.push(this);
31341 onClick: function(e, el)
31343 var time = this.endTimer - this.startTimer;
31347 e.preventDefault();
31352 if(!this.preventDefault){
31356 e.preventDefault();
31357 this.fireEvent('click', this);
31360 enter: function(e, el)
31362 e.preventDefault();
31364 if(!this.isFitContainer){
31368 if(this.bgimage.length && this.html.length){
31369 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31373 leave: function(e, el)
31375 e.preventDefault();
31377 if(!this.isFitContainer){
31381 if(this.bgimage.length && this.html.length){
31382 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31386 onTouchStart: function(e, el)
31388 // e.preventDefault();
31390 this.touchmoved = false;
31392 if(!this.isFitContainer){
31396 if(!this.bgimage.length || !this.html.length){
31400 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31402 this.timer = new Date().getTime();
31406 onTouchMove: function(e, el)
31408 this.touchmoved = true;
31411 onContextMenu : function(e,el)
31413 e.preventDefault();
31414 e.stopPropagation();
31418 onTouchEnd: function(e, el)
31420 // e.preventDefault();
31422 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31429 if(!this.bgimage.length || !this.html.length){
31431 if(this.href.length){
31432 window.location.href = this.href;
31438 if(!this.isFitContainer){
31442 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31444 window.location.href = this.href;
31459 * @class Roo.bootstrap.Brick
31460 * @extends Roo.bootstrap.Component
31461 * Bootstrap Brick class
31464 * Create a new Brick
31465 * @param {Object} config The config object
31468 Roo.bootstrap.Brick = function(config){
31469 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31475 * When a Brick is click
31476 * @param {Roo.bootstrap.Brick} this
31477 * @param {Roo.EventObject} e
31483 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
31486 * @cfg {String} title
31490 * @cfg {String} html
31494 * @cfg {String} bgimage
31498 * @cfg {String} cls
31502 * @cfg {String} href
31506 * @cfg {String} video
31510 * @cfg {Boolean} square
31514 getAutoCreate : function()
31516 var cls = 'roo-brick';
31518 if(this.href.length){
31519 cls += ' roo-brick-link';
31522 if(this.bgimage.length){
31523 cls += ' roo-brick-image';
31526 if(!this.html.length && !this.bgimage.length){
31527 cls += ' roo-brick-center-title';
31530 if(!this.html.length && this.bgimage.length){
31531 cls += ' roo-brick-bottom-title';
31535 cls += ' ' + this.cls;
31539 tag: (this.href.length) ? 'a' : 'div',
31544 cls: 'roo-brick-paragraph',
31550 if(this.href.length){
31551 cfg.href = this.href;
31554 var cn = cfg.cn[0].cn;
31556 if(this.title.length){
31559 cls: 'roo-brick-title',
31564 if(this.html.length){
31567 cls: 'roo-brick-text',
31574 if(this.bgimage.length){
31577 cls: 'roo-brick-image-view',
31585 initEvents: function()
31587 if(this.title.length || this.html.length){
31588 this.el.on('mouseenter' ,this.enter, this);
31589 this.el.on('mouseleave', this.leave, this);
31593 Roo.EventManager.onWindowResize(this.resize, this);
31598 resize : function()
31600 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31602 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31604 if(this.bgimage.length){
31605 var image = this.el.select('.roo-brick-image-view', true).first();
31606 image.setWidth(paragraph.getWidth());
31607 image.setHeight(paragraph.getWidth());
31609 this.el.setHeight(paragraph.getWidth());
31615 enter: function(e, el)
31617 e.preventDefault();
31619 if(this.bgimage.length){
31620 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31621 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31625 leave: function(e, el)
31627 e.preventDefault();
31629 if(this.bgimage.length){
31630 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31631 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31647 * @class Roo.bootstrap.NumberField
31648 * @extends Roo.bootstrap.Input
31649 * Bootstrap NumberField class
31655 * Create a new NumberField
31656 * @param {Object} config The config object
31659 Roo.bootstrap.NumberField = function(config){
31660 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
31663 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
31666 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
31668 allowDecimals : true,
31670 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
31672 decimalSeparator : ".",
31674 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
31676 decimalPrecision : 2,
31678 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
31680 allowNegative : true,
31682 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
31684 minValue : Number.NEGATIVE_INFINITY,
31686 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
31688 maxValue : Number.MAX_VALUE,
31690 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
31692 minText : "The minimum value for this field is {0}",
31694 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
31696 maxText : "The maximum value for this field is {0}",
31698 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
31699 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
31701 nanText : "{0} is not a valid number",
31703 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
31708 initEvents : function()
31710 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
31712 var allowed = "0123456789";
31714 if(this.allowDecimals){
31715 allowed += this.decimalSeparator;
31718 if(this.allowNegative){
31722 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
31724 var keyPress = function(e){
31726 var k = e.getKey();
31728 var c = e.getCharCode();
31731 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
31732 allowed.indexOf(String.fromCharCode(c)) === -1
31738 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
31742 if(allowed.indexOf(String.fromCharCode(c)) === -1){
31747 this.el.on("keypress", keyPress, this);
31750 validateValue : function(value)
31753 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
31757 var num = this.parseValue(value);
31760 this.markInvalid(String.format(this.nanText, value));
31764 if(num < this.minValue){
31765 this.markInvalid(String.format(this.minText, this.minValue));
31769 if(num > this.maxValue){
31770 this.markInvalid(String.format(this.maxText, this.maxValue));
31777 getValue : function()
31779 return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
31782 parseValue : function(value)
31784 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
31785 return isNaN(value) ? '' : value;
31788 fixPrecision : function(value)
31790 var nan = isNaN(value);
31792 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
31793 return nan ? '' : value;
31795 return parseFloat(value).toFixed(this.decimalPrecision);
31798 setValue : function(v)
31800 v = this.fixPrecision(v);
31801 Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
31804 decimalPrecisionFcn : function(v)
31806 return Math.floor(v);
31809 beforeBlur : function()
31815 var v = this.parseValue(this.getRawValue());
31830 * @class Roo.bootstrap.DocumentSlider
31831 * @extends Roo.bootstrap.Component
31832 * Bootstrap DocumentSlider class
31835 * Create a new DocumentViewer
31836 * @param {Object} config The config object
31839 Roo.bootstrap.DocumentSlider = function(config){
31840 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
31845 * Fire after initEvent
31846 * @param {Roo.bootstrap.DocumentViewer} this
31852 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
31854 getAutoCreate : function()
31858 cls : 'roo-document-slider',
31862 cls : 'roo-document-slider-body',
31866 cls : 'roo-document-slider-prev',
31870 cls : 'fa fa-chevron-left'
31876 cls : 'roo-document-slider-thumb',
31880 cls : 'roo-document-slider-image'
31886 cls : 'roo-document-slider-next',
31890 cls : 'fa fa-chevron-right'
31902 initEvents : function()
31904 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
31905 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31907 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
31908 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31910 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
31911 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31914 initial : function()
31916 this.fireEvent('initial', this);
31921 * Ext JS Library 1.1.1
31922 * Copyright(c) 2006-2007, Ext JS, LLC.
31924 * Originally Released Under LGPL - original licence link has changed is not relivant.
31927 * <script type="text/javascript">
31932 * @class Roo.bootstrap.SplitBar
31933 * @extends Roo.util.Observable
31934 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31938 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31939 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31940 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31941 split.minSize = 100;
31942 split.maxSize = 600;
31943 split.animate = true;
31944 split.on('moved', splitterMoved);
31947 * Create a new SplitBar
31948 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
31949 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
31950 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31951 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
31952 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31953 position of the SplitBar).
31955 Roo.bootstrap.SplitBar = function(cfg){
31960 // dragElement : elm
31961 // resizingElement: el,
31963 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31964 // placement : Roo.bootstrap.SplitBar.LEFT ,
31965 // existingProxy ???
31968 this.el = Roo.get(cfg.dragElement, true);
31969 this.el.dom.unselectable = "on";
31971 this.resizingEl = Roo.get(cfg.resizingElement, true);
31975 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31976 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31979 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31982 * The minimum size of the resizing element. (Defaults to 0)
31988 * The maximum size of the resizing element. (Defaults to 2000)
31991 this.maxSize = 2000;
31994 * Whether to animate the transition to the new size
31997 this.animate = false;
32000 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32003 this.useShim = false;
32008 if(!cfg.existingProxy){
32010 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32012 this.proxy = Roo.get(cfg.existingProxy).dom;
32015 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32018 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32021 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32024 this.dragSpecs = {};
32027 * @private The adapter to use to positon and resize elements
32029 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32030 this.adapter.init(this);
32032 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32034 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32035 this.el.addClass("roo-splitbar-h");
32038 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32039 this.el.addClass("roo-splitbar-v");
32045 * Fires when the splitter is moved (alias for {@link #event-moved})
32046 * @param {Roo.bootstrap.SplitBar} this
32047 * @param {Number} newSize the new width or height
32052 * Fires when the splitter is moved
32053 * @param {Roo.bootstrap.SplitBar} this
32054 * @param {Number} newSize the new width or height
32058 * @event beforeresize
32059 * Fires before the splitter is dragged
32060 * @param {Roo.bootstrap.SplitBar} this
32062 "beforeresize" : true,
32064 "beforeapply" : true
32067 Roo.util.Observable.call(this);
32070 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32071 onStartProxyDrag : function(x, y){
32072 this.fireEvent("beforeresize", this);
32074 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
32076 o.enableDisplayMode("block");
32077 // all splitbars share the same overlay
32078 Roo.bootstrap.SplitBar.prototype.overlay = o;
32080 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32081 this.overlay.show();
32082 Roo.get(this.proxy).setDisplayed("block");
32083 var size = this.adapter.getElementSize(this);
32084 this.activeMinSize = this.getMinimumSize();;
32085 this.activeMaxSize = this.getMaximumSize();;
32086 var c1 = size - this.activeMinSize;
32087 var c2 = Math.max(this.activeMaxSize - size, 0);
32088 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32089 this.dd.resetConstraints();
32090 this.dd.setXConstraint(
32091 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
32092 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32094 this.dd.setYConstraint(0, 0);
32096 this.dd.resetConstraints();
32097 this.dd.setXConstraint(0, 0);
32098 this.dd.setYConstraint(
32099 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
32100 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32103 this.dragSpecs.startSize = size;
32104 this.dragSpecs.startPoint = [x, y];
32105 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32109 * @private Called after the drag operation by the DDProxy
32111 onEndProxyDrag : function(e){
32112 Roo.get(this.proxy).setDisplayed(false);
32113 var endPoint = Roo.lib.Event.getXY(e);
32115 this.overlay.hide();
32118 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32119 newSize = this.dragSpecs.startSize +
32120 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32121 endPoint[0] - this.dragSpecs.startPoint[0] :
32122 this.dragSpecs.startPoint[0] - endPoint[0]
32125 newSize = this.dragSpecs.startSize +
32126 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32127 endPoint[1] - this.dragSpecs.startPoint[1] :
32128 this.dragSpecs.startPoint[1] - endPoint[1]
32131 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32132 if(newSize != this.dragSpecs.startSize){
32133 if(this.fireEvent('beforeapply', this, newSize) !== false){
32134 this.adapter.setElementSize(this, newSize);
32135 this.fireEvent("moved", this, newSize);
32136 this.fireEvent("resize", this, newSize);
32142 * Get the adapter this SplitBar uses
32143 * @return The adapter object
32145 getAdapter : function(){
32146 return this.adapter;
32150 * Set the adapter this SplitBar uses
32151 * @param {Object} adapter A SplitBar adapter object
32153 setAdapter : function(adapter){
32154 this.adapter = adapter;
32155 this.adapter.init(this);
32159 * Gets the minimum size for the resizing element
32160 * @return {Number} The minimum size
32162 getMinimumSize : function(){
32163 return this.minSize;
32167 * Sets the minimum size for the resizing element
32168 * @param {Number} minSize The minimum size
32170 setMinimumSize : function(minSize){
32171 this.minSize = minSize;
32175 * Gets the maximum size for the resizing element
32176 * @return {Number} The maximum size
32178 getMaximumSize : function(){
32179 return this.maxSize;
32183 * Sets the maximum size for the resizing element
32184 * @param {Number} maxSize The maximum size
32186 setMaximumSize : function(maxSize){
32187 this.maxSize = maxSize;
32191 * Sets the initialize size for the resizing element
32192 * @param {Number} size The initial size
32194 setCurrentSize : function(size){
32195 var oldAnimate = this.animate;
32196 this.animate = false;
32197 this.adapter.setElementSize(this, size);
32198 this.animate = oldAnimate;
32202 * Destroy this splitbar.
32203 * @param {Boolean} removeEl True to remove the element
32205 destroy : function(removeEl){
32207 this.shim.remove();
32210 this.proxy.parentNode.removeChild(this.proxy);
32218 * @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.
32220 Roo.bootstrap.SplitBar.createProxy = function(dir){
32221 var proxy = new Roo.Element(document.createElement("div"));
32222 proxy.unselectable();
32223 var cls = 'roo-splitbar-proxy';
32224 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
32225 document.body.appendChild(proxy.dom);
32230 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
32231 * Default Adapter. It assumes the splitter and resizing element are not positioned
32232 * elements and only gets/sets the width of the element. Generally used for table based layouts.
32234 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
32237 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
32238 // do nothing for now
32239 init : function(s){
32243 * Called before drag operations to get the current size of the resizing element.
32244 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32246 getElementSize : function(s){
32247 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32248 return s.resizingEl.getWidth();
32250 return s.resizingEl.getHeight();
32255 * Called after drag operations to set the size of the resizing element.
32256 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32257 * @param {Number} newSize The new size to set
32258 * @param {Function} onComplete A function to be invoked when resizing is complete
32260 setElementSize : function(s, newSize, onComplete){
32261 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32263 s.resizingEl.setWidth(newSize);
32265 onComplete(s, newSize);
32268 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
32273 s.resizingEl.setHeight(newSize);
32275 onComplete(s, newSize);
32278 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
32285 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
32286 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
32287 * Adapter that moves the splitter element to align with the resized sizing element.
32288 * Used with an absolute positioned SplitBar.
32289 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
32290 * document.body, make sure you assign an id to the body element.
32292 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
32293 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32294 this.container = Roo.get(container);
32297 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
32298 init : function(s){
32299 this.basic.init(s);
32302 getElementSize : function(s){
32303 return this.basic.getElementSize(s);
32306 setElementSize : function(s, newSize, onComplete){
32307 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32310 moveSplitter : function(s){
32311 var yes = Roo.bootstrap.SplitBar;
32312 switch(s.placement){
32314 s.el.setX(s.resizingEl.getRight());
32317 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32320 s.el.setY(s.resizingEl.getBottom());
32323 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32330 * Orientation constant - Create a vertical SplitBar
32334 Roo.bootstrap.SplitBar.VERTICAL = 1;
32337 * Orientation constant - Create a horizontal SplitBar
32341 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32344 * Placement constant - The resizing element is to the left of the splitter element
32348 Roo.bootstrap.SplitBar.LEFT = 1;
32351 * Placement constant - The resizing element is to the right of the splitter element
32355 Roo.bootstrap.SplitBar.RIGHT = 2;
32358 * Placement constant - The resizing element is positioned above the splitter element
32362 Roo.bootstrap.SplitBar.TOP = 3;
32365 * Placement constant - The resizing element is positioned under splitter element
32369 Roo.bootstrap.SplitBar.BOTTOM = 4;
32370 Roo.namespace("Roo.bootstrap.layout");/*
32372 * Ext JS Library 1.1.1
32373 * Copyright(c) 2006-2007, Ext JS, LLC.
32375 * Originally Released Under LGPL - original licence link has changed is not relivant.
32378 * <script type="text/javascript">
32382 * @class Roo.bootstrap.layout.Manager
32383 * @extends Roo.bootstrap.Component
32384 * Base class for layout managers.
32386 Roo.bootstrap.layout.Manager = function(config)
32388 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32394 /** false to disable window resize monitoring @type Boolean */
32395 this.monitorWindowResize = true;
32400 * Fires when a layout is performed.
32401 * @param {Roo.LayoutManager} this
32405 * @event regionresized
32406 * Fires when the user resizes a region.
32407 * @param {Roo.LayoutRegion} region The resized region
32408 * @param {Number} newSize The new size (width for east/west, height for north/south)
32410 "regionresized" : true,
32412 * @event regioncollapsed
32413 * Fires when a region is collapsed.
32414 * @param {Roo.LayoutRegion} region The collapsed region
32416 "regioncollapsed" : true,
32418 * @event regionexpanded
32419 * Fires when a region is expanded.
32420 * @param {Roo.LayoutRegion} region The expanded region
32422 "regionexpanded" : true
32424 this.updating = false;
32427 this.el = Roo.get(config.el);
32433 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32438 monitorWindowResize : true,
32444 onRender : function(ct, position)
32447 this.el = Roo.get(ct);
32450 //this.fireEvent('render',this);
32454 initEvents: function()
32458 // ie scrollbar fix
32459 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32460 document.body.scroll = "no";
32461 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32462 this.el.position('relative');
32464 this.id = this.el.id;
32465 this.el.addClass("roo-layout-container");
32466 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32467 if(this.el.dom != document.body ) {
32468 this.el.on('resize', this.layout,this);
32469 this.el.on('show', this.layout,this);
32475 * Returns true if this layout is currently being updated
32476 * @return {Boolean}
32478 isUpdating : function(){
32479 return this.updating;
32483 * Suspend the LayoutManager from doing auto-layouts while
32484 * making multiple add or remove calls
32486 beginUpdate : function(){
32487 this.updating = true;
32491 * Restore auto-layouts and optionally disable the manager from performing a layout
32492 * @param {Boolean} noLayout true to disable a layout update
32494 endUpdate : function(noLayout){
32495 this.updating = false;
32501 layout: function(){
32505 onRegionResized : function(region, newSize){
32506 this.fireEvent("regionresized", region, newSize);
32510 onRegionCollapsed : function(region){
32511 this.fireEvent("regioncollapsed", region);
32514 onRegionExpanded : function(region){
32515 this.fireEvent("regionexpanded", region);
32519 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32520 * performs box-model adjustments.
32521 * @return {Object} The size as an object {width: (the width), height: (the height)}
32523 getViewSize : function()
32526 if(this.el.dom != document.body){
32527 size = this.el.getSize();
32529 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32531 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32532 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32537 * Returns the Element this layout is bound to.
32538 * @return {Roo.Element}
32540 getEl : function(){
32545 * Returns the specified region.
32546 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32547 * @return {Roo.LayoutRegion}
32549 getRegion : function(target){
32550 return this.regions[target.toLowerCase()];
32553 onWindowResize : function(){
32554 if(this.monitorWindowResize){
32561 * Ext JS Library 1.1.1
32562 * Copyright(c) 2006-2007, Ext JS, LLC.
32564 * Originally Released Under LGPL - original licence link has changed is not relivant.
32567 * <script type="text/javascript">
32570 * @class Roo.bootstrap.layout.Border
32571 * @extends Roo.bootstrap.layout.Manager
32572 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32573 * please see: examples/bootstrap/nested.html<br><br>
32575 <b>The container the layout is rendered into can be either the body element or any other element.
32576 If it is not the body element, the container needs to either be an absolute positioned element,
32577 or you will need to add "position:relative" to the css of the container. You will also need to specify
32578 the container size if it is not the body element.</b>
32581 * Create a new Border
32582 * @param {Object} config Configuration options
32584 Roo.bootstrap.layout.Border = function(config){
32585 config = config || {};
32586 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32590 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32591 if(config[region]){
32592 config[region].region = region;
32593 this.addRegion(config[region]);
32599 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
32601 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32603 * Creates and adds a new region if it doesn't already exist.
32604 * @param {String} target The target region key (north, south, east, west or center).
32605 * @param {Object} config The regions config object
32606 * @return {BorderLayoutRegion} The new region
32608 addRegion : function(config)
32610 if(!this.regions[config.region]){
32611 var r = this.factory(config);
32612 this.bindRegion(r);
32614 return this.regions[config.region];
32618 bindRegion : function(r){
32619 this.regions[r.config.region] = r;
32621 r.on("visibilitychange", this.layout, this);
32622 r.on("paneladded", this.layout, this);
32623 r.on("panelremoved", this.layout, this);
32624 r.on("invalidated", this.layout, this);
32625 r.on("resized", this.onRegionResized, this);
32626 r.on("collapsed", this.onRegionCollapsed, this);
32627 r.on("expanded", this.onRegionExpanded, this);
32631 * Performs a layout update.
32633 layout : function()
32635 if(this.updating) {
32639 // render all the rebions if they have not been done alreayd?
32640 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32641 if(this.regions[region] && !this.regions[region].bodyEl){
32642 this.regions[region].onRender(this.el)
32646 var size = this.getViewSize();
32647 var w = size.width;
32648 var h = size.height;
32653 //var x = 0, y = 0;
32655 var rs = this.regions;
32656 var north = rs["north"];
32657 var south = rs["south"];
32658 var west = rs["west"];
32659 var east = rs["east"];
32660 var center = rs["center"];
32661 //if(this.hideOnLayout){ // not supported anymore
32662 //c.el.setStyle("display", "none");
32664 if(north && north.isVisible()){
32665 var b = north.getBox();
32666 var m = north.getMargins();
32667 b.width = w - (m.left+m.right);
32670 centerY = b.height + b.y + m.bottom;
32671 centerH -= centerY;
32672 north.updateBox(this.safeBox(b));
32674 if(south && south.isVisible()){
32675 var b = south.getBox();
32676 var m = south.getMargins();
32677 b.width = w - (m.left+m.right);
32679 var totalHeight = (b.height + m.top + m.bottom);
32680 b.y = h - totalHeight + m.top;
32681 centerH -= totalHeight;
32682 south.updateBox(this.safeBox(b));
32684 if(west && west.isVisible()){
32685 var b = west.getBox();
32686 var m = west.getMargins();
32687 b.height = centerH - (m.top+m.bottom);
32689 b.y = centerY + m.top;
32690 var totalWidth = (b.width + m.left + m.right);
32691 centerX += totalWidth;
32692 centerW -= totalWidth;
32693 west.updateBox(this.safeBox(b));
32695 if(east && east.isVisible()){
32696 var b = east.getBox();
32697 var m = east.getMargins();
32698 b.height = centerH - (m.top+m.bottom);
32699 var totalWidth = (b.width + m.left + m.right);
32700 b.x = w - totalWidth + m.left;
32701 b.y = centerY + m.top;
32702 centerW -= totalWidth;
32703 east.updateBox(this.safeBox(b));
32706 var m = center.getMargins();
32708 x: centerX + m.left,
32709 y: centerY + m.top,
32710 width: centerW - (m.left+m.right),
32711 height: centerH - (m.top+m.bottom)
32713 //if(this.hideOnLayout){
32714 //center.el.setStyle("display", "block");
32716 center.updateBox(this.safeBox(centerBox));
32719 this.fireEvent("layout", this);
32723 safeBox : function(box){
32724 box.width = Math.max(0, box.width);
32725 box.height = Math.max(0, box.height);
32730 * Adds a ContentPanel (or subclass) to this layout.
32731 * @param {String} target The target region key (north, south, east, west or center).
32732 * @param {Roo.ContentPanel} panel The panel to add
32733 * @return {Roo.ContentPanel} The added panel
32735 add : function(target, panel){
32737 target = target.toLowerCase();
32738 return this.regions[target].add(panel);
32742 * Remove a ContentPanel (or subclass) to this layout.
32743 * @param {String} target The target region key (north, south, east, west or center).
32744 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32745 * @return {Roo.ContentPanel} The removed panel
32747 remove : function(target, panel){
32748 target = target.toLowerCase();
32749 return this.regions[target].remove(panel);
32753 * Searches all regions for a panel with the specified id
32754 * @param {String} panelId
32755 * @return {Roo.ContentPanel} The panel or null if it wasn't found
32757 findPanel : function(panelId){
32758 var rs = this.regions;
32759 for(var target in rs){
32760 if(typeof rs[target] != "function"){
32761 var p = rs[target].getPanel(panelId);
32771 * Searches all regions for a panel with the specified id and activates (shows) it.
32772 * @param {String/ContentPanel} panelId The panels id or the panel itself
32773 * @return {Roo.ContentPanel} The shown panel or null
32775 showPanel : function(panelId) {
32776 var rs = this.regions;
32777 for(var target in rs){
32778 var r = rs[target];
32779 if(typeof r != "function"){
32780 if(r.hasPanel(panelId)){
32781 return r.showPanel(panelId);
32789 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32790 * @param {Roo.state.Provider} provider (optional) An alternate state provider
32793 restoreState : function(provider){
32795 provider = Roo.state.Manager;
32797 var sm = new Roo.LayoutStateManager();
32798 sm.init(this, provider);
32804 * Adds a xtype elements to the layout.
32808 xtype : 'ContentPanel',
32815 xtype : 'NestedLayoutPanel',
32821 items : [ ... list of content panels or nested layout panels.. ]
32825 * @param {Object} cfg Xtype definition of item to add.
32827 addxtype : function(cfg)
32829 // basically accepts a pannel...
32830 // can accept a layout region..!?!?
32831 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32834 // theory? children can only be panels??
32836 //if (!cfg.xtype.match(/Panel$/)) {
32841 if (typeof(cfg.region) == 'undefined') {
32842 Roo.log("Failed to add Panel, region was not set");
32846 var region = cfg.region;
32852 xitems = cfg.items;
32859 case 'Content': // ContentPanel (el, cfg)
32860 case 'Scroll': // ContentPanel (el, cfg)
32862 cfg.autoCreate = true;
32863 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32865 // var el = this.el.createChild();
32866 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32869 this.add(region, ret);
32873 case 'TreePanel': // our new panel!
32874 cfg.el = this.el.createChild();
32875 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32876 this.add(region, ret);
32881 // create a new Layout (which is a Border Layout...
32883 var clayout = cfg.layout;
32884 clayout.el = this.el.createChild();
32885 clayout.items = clayout.items || [];
32889 // replace this exitems with the clayout ones..
32890 xitems = clayout.items;
32892 // force background off if it's in center...
32893 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32894 cfg.background = false;
32896 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
32899 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32900 //console.log('adding nested layout panel ' + cfg.toSource());
32901 this.add(region, ret);
32902 nb = {}; /// find first...
32907 // needs grid and region
32909 //var el = this.getRegion(region).el.createChild();
32911 *var el = this.el.createChild();
32912 // create the grid first...
32913 cfg.grid.container = el;
32914 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
32917 if (region == 'center' && this.active ) {
32918 cfg.background = false;
32921 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32923 this.add(region, ret);
32925 if (cfg.background) {
32926 // render grid on panel activation (if panel background)
32927 ret.on('activate', function(gp) {
32928 if (!gp.grid.rendered) {
32929 // gp.grid.render(el);
32933 // cfg.grid.render(el);
32939 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
32940 // it was the old xcomponent building that caused this before.
32941 // espeically if border is the top element in the tree.
32951 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32953 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32954 this.add(region, ret);
32958 throw "Can not add '" + cfg.xtype + "' to Border";
32964 this.beginUpdate();
32968 Roo.each(xitems, function(i) {
32969 region = nb && i.region ? i.region : false;
32971 var add = ret.addxtype(i);
32974 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32975 if (!i.background) {
32976 abn[region] = nb[region] ;
32983 // make the last non-background panel active..
32984 //if (nb) { Roo.log(abn); }
32987 for(var r in abn) {
32988 region = this.getRegion(r);
32990 // tried using nb[r], but it does not work..
32992 region.showPanel(abn[r]);
33003 factory : function(cfg)
33006 var validRegions = Roo.bootstrap.layout.Border.regions;
33008 var target = cfg.region;
33011 var r = Roo.bootstrap.layout;
33015 return new r.North(cfg);
33017 return new r.South(cfg);
33019 return new r.East(cfg);
33021 return new r.West(cfg);
33023 return new r.Center(cfg);
33025 throw 'Layout region "'+target+'" not supported.';
33032 * Ext JS Library 1.1.1
33033 * Copyright(c) 2006-2007, Ext JS, LLC.
33035 * Originally Released Under LGPL - original licence link has changed is not relivant.
33038 * <script type="text/javascript">
33042 * @class Roo.bootstrap.layout.Basic
33043 * @extends Roo.util.Observable
33044 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33045 * and does not have a titlebar, tabs or any other features. All it does is size and position
33046 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33047 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
33048 * @cfg {string} region the region that it inhabits..
33049 * @cfg {bool} skipConfig skip config?
33053 Roo.bootstrap.layout.Basic = function(config){
33055 this.mgr = config.mgr;
33057 this.position = config.region;
33059 var skipConfig = config.skipConfig;
33063 * @scope Roo.BasicLayoutRegion
33067 * @event beforeremove
33068 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33069 * @param {Roo.LayoutRegion} this
33070 * @param {Roo.ContentPanel} panel The panel
33071 * @param {Object} e The cancel event object
33073 "beforeremove" : true,
33075 * @event invalidated
33076 * Fires when the layout for this region is changed.
33077 * @param {Roo.LayoutRegion} this
33079 "invalidated" : true,
33081 * @event visibilitychange
33082 * Fires when this region is shown or hidden
33083 * @param {Roo.LayoutRegion} this
33084 * @param {Boolean} visibility true or false
33086 "visibilitychange" : true,
33088 * @event paneladded
33089 * Fires when a panel is added.
33090 * @param {Roo.LayoutRegion} this
33091 * @param {Roo.ContentPanel} panel The panel
33093 "paneladded" : true,
33095 * @event panelremoved
33096 * Fires when a panel is removed.
33097 * @param {Roo.LayoutRegion} this
33098 * @param {Roo.ContentPanel} panel The panel
33100 "panelremoved" : true,
33102 * @event beforecollapse
33103 * Fires when this region before collapse.
33104 * @param {Roo.LayoutRegion} this
33106 "beforecollapse" : true,
33109 * Fires when this region is collapsed.
33110 * @param {Roo.LayoutRegion} this
33112 "collapsed" : true,
33115 * Fires when this region is expanded.
33116 * @param {Roo.LayoutRegion} this
33121 * Fires when this region is slid into view.
33122 * @param {Roo.LayoutRegion} this
33124 "slideshow" : true,
33127 * Fires when this region slides out of view.
33128 * @param {Roo.LayoutRegion} this
33130 "slidehide" : true,
33132 * @event panelactivated
33133 * Fires when a panel is activated.
33134 * @param {Roo.LayoutRegion} this
33135 * @param {Roo.ContentPanel} panel The activated panel
33137 "panelactivated" : true,
33140 * Fires when the user resizes this region.
33141 * @param {Roo.LayoutRegion} this
33142 * @param {Number} newSize The new size (width for east/west, height for north/south)
33146 /** A collection of panels in this region. @type Roo.util.MixedCollection */
33147 this.panels = new Roo.util.MixedCollection();
33148 this.panels.getKey = this.getPanelId.createDelegate(this);
33150 this.activePanel = null;
33151 // ensure listeners are added...
33153 if (config.listeners || config.events) {
33154 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33155 listeners : config.listeners || {},
33156 events : config.events || {}
33160 if(skipConfig !== true){
33161 this.applyConfig(config);
33165 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33167 getPanelId : function(p){
33171 applyConfig : function(config){
33172 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33173 this.config = config;
33178 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
33179 * the width, for horizontal (north, south) the height.
33180 * @param {Number} newSize The new width or height
33182 resizeTo : function(newSize){
33183 var el = this.el ? this.el :
33184 (this.activePanel ? this.activePanel.getEl() : null);
33186 switch(this.position){
33189 el.setWidth(newSize);
33190 this.fireEvent("resized", this, newSize);
33194 el.setHeight(newSize);
33195 this.fireEvent("resized", this, newSize);
33201 getBox : function(){
33202 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33205 getMargins : function(){
33206 return this.margins;
33209 updateBox : function(box){
33211 var el = this.activePanel.getEl();
33212 el.dom.style.left = box.x + "px";
33213 el.dom.style.top = box.y + "px";
33214 this.activePanel.setSize(box.width, box.height);
33218 * Returns the container element for this region.
33219 * @return {Roo.Element}
33221 getEl : function(){
33222 return this.activePanel;
33226 * Returns true if this region is currently visible.
33227 * @return {Boolean}
33229 isVisible : function(){
33230 return this.activePanel ? true : false;
33233 setActivePanel : function(panel){
33234 panel = this.getPanel(panel);
33235 if(this.activePanel && this.activePanel != panel){
33236 this.activePanel.setActiveState(false);
33237 this.activePanel.getEl().setLeftTop(-10000,-10000);
33239 this.activePanel = panel;
33240 panel.setActiveState(true);
33242 panel.setSize(this.box.width, this.box.height);
33244 this.fireEvent("panelactivated", this, panel);
33245 this.fireEvent("invalidated");
33249 * Show the specified panel.
33250 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33251 * @return {Roo.ContentPanel} The shown panel or null
33253 showPanel : function(panel){
33254 panel = this.getPanel(panel);
33256 this.setActivePanel(panel);
33262 * Get the active panel for this region.
33263 * @return {Roo.ContentPanel} The active panel or null
33265 getActivePanel : function(){
33266 return this.activePanel;
33270 * Add the passed ContentPanel(s)
33271 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33272 * @return {Roo.ContentPanel} The panel added (if only one was added)
33274 add : function(panel){
33275 if(arguments.length > 1){
33276 for(var i = 0, len = arguments.length; i < len; i++) {
33277 this.add(arguments[i]);
33281 if(this.hasPanel(panel)){
33282 this.showPanel(panel);
33285 var el = panel.getEl();
33286 if(el.dom.parentNode != this.mgr.el.dom){
33287 this.mgr.el.dom.appendChild(el.dom);
33289 if(panel.setRegion){
33290 panel.setRegion(this);
33292 this.panels.add(panel);
33293 el.setStyle("position", "absolute");
33294 if(!panel.background){
33295 this.setActivePanel(panel);
33296 if(this.config.initialSize && this.panels.getCount()==1){
33297 this.resizeTo(this.config.initialSize);
33300 this.fireEvent("paneladded", this, panel);
33305 * Returns true if the panel is in this region.
33306 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33307 * @return {Boolean}
33309 hasPanel : function(panel){
33310 if(typeof panel == "object"){ // must be panel obj
33311 panel = panel.getId();
33313 return this.getPanel(panel) ? true : false;
33317 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33318 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33319 * @param {Boolean} preservePanel Overrides the config preservePanel option
33320 * @return {Roo.ContentPanel} The panel that was removed
33322 remove : function(panel, preservePanel){
33323 panel = this.getPanel(panel);
33328 this.fireEvent("beforeremove", this, panel, e);
33329 if(e.cancel === true){
33332 var panelId = panel.getId();
33333 this.panels.removeKey(panelId);
33338 * Returns the panel specified or null if it's not in this region.
33339 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33340 * @return {Roo.ContentPanel}
33342 getPanel : function(id){
33343 if(typeof id == "object"){ // must be panel obj
33346 return this.panels.get(id);
33350 * Returns this regions position (north/south/east/west/center).
33353 getPosition: function(){
33354 return this.position;
33358 * Ext JS Library 1.1.1
33359 * Copyright(c) 2006-2007, Ext JS, LLC.
33361 * Originally Released Under LGPL - original licence link has changed is not relivant.
33364 * <script type="text/javascript">
33368 * @class Roo.bootstrap.layout.Region
33369 * @extends Roo.bootstrap.layout.Basic
33370 * This class represents a region in a layout manager.
33372 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33373 * @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})
33374 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
33375 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
33376 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
33377 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
33378 * @cfg {String} title The title for the region (overrides panel titles)
33379 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
33380 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33381 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
33382 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33383 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
33384 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33385 * the space available, similar to FireFox 1.5 tabs (defaults to false)
33386 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
33387 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
33388 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
33390 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
33391 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
33392 * @cfg {Boolean} disableTabTips True to disable tab tooltips
33393 * @cfg {Number} width For East/West panels
33394 * @cfg {Number} height For North/South panels
33395 * @cfg {Boolean} split To show the splitter
33396 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
33398 * @cfg {string} cls Extra CSS classes to add to region
33400 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
33401 * @cfg {string} region the region that it inhabits..
33404 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
33405 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
33407 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
33408 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
33409 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
33411 Roo.bootstrap.layout.Region = function(config)
33413 this.applyConfig(config);
33415 var mgr = config.mgr;
33416 var pos = config.region;
33417 config.skipConfig = true;
33418 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33421 this.onRender(mgr.el);
33424 this.visible = true;
33425 this.collapsed = false;
33426 this.unrendered_panels = [];
33429 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33431 position: '', // set by wrapper (eg. north/south etc..)
33432 unrendered_panels : null, // unrendered panels.
33433 createBody : function(){
33434 /** This region's body element
33435 * @type Roo.Element */
33436 this.bodyEl = this.el.createChild({
33438 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33442 onRender: function(ctr, pos)
33444 var dh = Roo.DomHelper;
33445 /** This region's container element
33446 * @type Roo.Element */
33447 this.el = dh.append(ctr.dom, {
33449 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33451 /** This region's title element
33452 * @type Roo.Element */
33454 this.titleEl = dh.append(this.el.dom,
33457 unselectable: "on",
33458 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33460 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
33461 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33464 this.titleEl.enableDisplayMode();
33465 /** This region's title text element
33466 * @type HTMLElement */
33467 this.titleTextEl = this.titleEl.dom.firstChild;
33468 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33470 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33471 this.closeBtn.enableDisplayMode();
33472 this.closeBtn.on("click", this.closeClicked, this);
33473 this.closeBtn.hide();
33475 this.createBody(this.config);
33476 if(this.config.hideWhenEmpty){
33478 this.on("paneladded", this.validateVisibility, this);
33479 this.on("panelremoved", this.validateVisibility, this);
33481 if(this.autoScroll){
33482 this.bodyEl.setStyle("overflow", "auto");
33484 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33486 //if(c.titlebar !== false){
33487 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33488 this.titleEl.hide();
33490 this.titleEl.show();
33491 if(this.config.title){
33492 this.titleTextEl.innerHTML = this.config.title;
33496 if(this.config.collapsed){
33497 this.collapse(true);
33499 if(this.config.hidden){
33503 if (this.unrendered_panels && this.unrendered_panels.length) {
33504 for (var i =0;i< this.unrendered_panels.length; i++) {
33505 this.add(this.unrendered_panels[i]);
33507 this.unrendered_panels = null;
33513 applyConfig : function(c)
33516 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33517 var dh = Roo.DomHelper;
33518 if(c.titlebar !== false){
33519 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33520 this.collapseBtn.on("click", this.collapse, this);
33521 this.collapseBtn.enableDisplayMode();
33523 if(c.showPin === true || this.showPin){
33524 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33525 this.stickBtn.enableDisplayMode();
33526 this.stickBtn.on("click", this.expand, this);
33527 this.stickBtn.hide();
33532 /** This region's collapsed element
33533 * @type Roo.Element */
33536 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33537 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33540 if(c.floatable !== false){
33541 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33542 this.collapsedEl.on("click", this.collapseClick, this);
33545 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33546 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33547 id: "message", unselectable: "on", style:{"float":"left"}});
33548 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33550 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33551 this.expandBtn.on("click", this.expand, this);
33555 if(this.collapseBtn){
33556 this.collapseBtn.setVisible(c.collapsible == true);
33559 this.cmargins = c.cmargins || this.cmargins ||
33560 (this.position == "west" || this.position == "east" ?
33561 {top: 0, left: 2, right:2, bottom: 0} :
33562 {top: 2, left: 0, right:0, bottom: 2});
33564 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33567 this.bottomTabs = c.tabPosition != "top";
33569 this.autoScroll = c.autoScroll || false;
33574 this.duration = c.duration || .30;
33575 this.slideDuration = c.slideDuration || .45;
33580 * Returns true if this region is currently visible.
33581 * @return {Boolean}
33583 isVisible : function(){
33584 return this.visible;
33588 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33589 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
33591 //setCollapsedTitle : function(title){
33592 // title = title || " ";
33593 // if(this.collapsedTitleTextEl){
33594 // this.collapsedTitleTextEl.innerHTML = title;
33598 getBox : function(){
33600 // if(!this.collapsed){
33601 b = this.el.getBox(false, true);
33603 // b = this.collapsedEl.getBox(false, true);
33608 getMargins : function(){
33609 return this.margins;
33610 //return this.collapsed ? this.cmargins : this.margins;
33613 highlight : function(){
33614 this.el.addClass("x-layout-panel-dragover");
33617 unhighlight : function(){
33618 this.el.removeClass("x-layout-panel-dragover");
33621 updateBox : function(box)
33623 if (!this.bodyEl) {
33624 return; // not rendered yet..
33628 if(!this.collapsed){
33629 this.el.dom.style.left = box.x + "px";
33630 this.el.dom.style.top = box.y + "px";
33631 this.updateBody(box.width, box.height);
33633 this.collapsedEl.dom.style.left = box.x + "px";
33634 this.collapsedEl.dom.style.top = box.y + "px";
33635 this.collapsedEl.setSize(box.width, box.height);
33638 this.tabs.autoSizeTabs();
33642 updateBody : function(w, h)
33645 this.el.setWidth(w);
33646 w -= this.el.getBorderWidth("rl");
33647 if(this.config.adjustments){
33648 w += this.config.adjustments[0];
33651 if(h !== null && h > 0){
33652 this.el.setHeight(h);
33653 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33654 h -= this.el.getBorderWidth("tb");
33655 if(this.config.adjustments){
33656 h += this.config.adjustments[1];
33658 this.bodyEl.setHeight(h);
33660 h = this.tabs.syncHeight(h);
33663 if(this.panelSize){
33664 w = w !== null ? w : this.panelSize.width;
33665 h = h !== null ? h : this.panelSize.height;
33667 if(this.activePanel){
33668 var el = this.activePanel.getEl();
33669 w = w !== null ? w : el.getWidth();
33670 h = h !== null ? h : el.getHeight();
33671 this.panelSize = {width: w, height: h};
33672 this.activePanel.setSize(w, h);
33674 if(Roo.isIE && this.tabs){
33675 this.tabs.el.repaint();
33680 * Returns the container element for this region.
33681 * @return {Roo.Element}
33683 getEl : function(){
33688 * Hides this region.
33691 //if(!this.collapsed){
33692 this.el.dom.style.left = "-2000px";
33695 // this.collapsedEl.dom.style.left = "-2000px";
33696 // this.collapsedEl.hide();
33698 this.visible = false;
33699 this.fireEvent("visibilitychange", this, false);
33703 * Shows this region if it was previously hidden.
33706 //if(!this.collapsed){
33709 // this.collapsedEl.show();
33711 this.visible = true;
33712 this.fireEvent("visibilitychange", this, true);
33715 closeClicked : function(){
33716 if(this.activePanel){
33717 this.remove(this.activePanel);
33721 collapseClick : function(e){
33723 e.stopPropagation();
33726 e.stopPropagation();
33732 * Collapses this region.
33733 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33736 collapse : function(skipAnim, skipCheck = false){
33737 if(this.collapsed) {
33741 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33743 this.collapsed = true;
33745 this.split.el.hide();
33747 if(this.config.animate && skipAnim !== true){
33748 this.fireEvent("invalidated", this);
33749 this.animateCollapse();
33751 this.el.setLocation(-20000,-20000);
33753 this.collapsedEl.show();
33754 this.fireEvent("collapsed", this);
33755 this.fireEvent("invalidated", this);
33761 animateCollapse : function(){
33766 * Expands this region if it was previously collapsed.
33767 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33768 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33771 expand : function(e, skipAnim){
33773 e.stopPropagation();
33775 if(!this.collapsed || this.el.hasActiveFx()) {
33779 this.afterSlideIn();
33782 this.collapsed = false;
33783 if(this.config.animate && skipAnim !== true){
33784 this.animateExpand();
33788 this.split.el.show();
33790 this.collapsedEl.setLocation(-2000,-2000);
33791 this.collapsedEl.hide();
33792 this.fireEvent("invalidated", this);
33793 this.fireEvent("expanded", this);
33797 animateExpand : function(){
33801 initTabs : function()
33803 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33805 var ts = new Roo.bootstrap.panel.Tabs({
33806 el: this.bodyEl.dom,
33807 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33808 disableTooltips: this.config.disableTabTips,
33809 toolbar : this.config.toolbar
33812 if(this.config.hideTabs){
33813 ts.stripWrap.setDisplayed(false);
33816 ts.resizeTabs = this.config.resizeTabs === true;
33817 ts.minTabWidth = this.config.minTabWidth || 40;
33818 ts.maxTabWidth = this.config.maxTabWidth || 250;
33819 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33820 ts.monitorResize = false;
33821 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33822 ts.bodyEl.addClass('roo-layout-tabs-body');
33823 this.panels.each(this.initPanelAsTab, this);
33826 initPanelAsTab : function(panel){
33827 var ti = this.tabs.addTab(
33831 this.config.closeOnTab && panel.isClosable(),
33834 if(panel.tabTip !== undefined){
33835 ti.setTooltip(panel.tabTip);
33837 ti.on("activate", function(){
33838 this.setActivePanel(panel);
33841 if(this.config.closeOnTab){
33842 ti.on("beforeclose", function(t, e){
33844 this.remove(panel);
33848 panel.tabItem = ti;
33853 updatePanelTitle : function(panel, title)
33855 if(this.activePanel == panel){
33856 this.updateTitle(title);
33859 var ti = this.tabs.getTab(panel.getEl().id);
33861 if(panel.tabTip !== undefined){
33862 ti.setTooltip(panel.tabTip);
33867 updateTitle : function(title){
33868 if(this.titleTextEl && !this.config.title){
33869 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
33873 setActivePanel : function(panel)
33875 panel = this.getPanel(panel);
33876 if(this.activePanel && this.activePanel != panel){
33877 this.activePanel.setActiveState(false);
33879 this.activePanel = panel;
33880 panel.setActiveState(true);
33881 if(this.panelSize){
33882 panel.setSize(this.panelSize.width, this.panelSize.height);
33885 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33887 this.updateTitle(panel.getTitle());
33889 this.fireEvent("invalidated", this);
33891 this.fireEvent("panelactivated", this, panel);
33895 * Shows the specified panel.
33896 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33897 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33899 showPanel : function(panel)
33901 panel = this.getPanel(panel);
33904 var tab = this.tabs.getTab(panel.getEl().id);
33905 if(tab.isHidden()){
33906 this.tabs.unhideTab(tab.id);
33910 this.setActivePanel(panel);
33917 * Get the active panel for this region.
33918 * @return {Roo.ContentPanel} The active panel or null
33920 getActivePanel : function(){
33921 return this.activePanel;
33924 validateVisibility : function(){
33925 if(this.panels.getCount() < 1){
33926 this.updateTitle(" ");
33927 this.closeBtn.hide();
33930 if(!this.isVisible()){
33937 * Adds the passed ContentPanel(s) to this region.
33938 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33939 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33941 add : function(panel)
33943 if(arguments.length > 1){
33944 for(var i = 0, len = arguments.length; i < len; i++) {
33945 this.add(arguments[i]);
33950 // if we have not been rendered yet, then we can not really do much of this..
33951 if (!this.bodyEl) {
33952 this.unrendered_panels.push(panel);
33959 if(this.hasPanel(panel)){
33960 this.showPanel(panel);
33963 panel.setRegion(this);
33964 this.panels.add(panel);
33965 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33966 // sinle panel - no tab...?? would it not be better to render it with the tabs,
33967 // and hide them... ???
33968 this.bodyEl.dom.appendChild(panel.getEl().dom);
33969 if(panel.background !== true){
33970 this.setActivePanel(panel);
33972 this.fireEvent("paneladded", this, panel);
33979 this.initPanelAsTab(panel);
33983 if(panel.background !== true){
33984 this.tabs.activate(panel.getEl().id);
33986 this.fireEvent("paneladded", this, panel);
33991 * Hides the tab for the specified panel.
33992 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33994 hidePanel : function(panel){
33995 if(this.tabs && (panel = this.getPanel(panel))){
33996 this.tabs.hideTab(panel.getEl().id);
34001 * Unhides the tab for a previously hidden panel.
34002 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34004 unhidePanel : function(panel){
34005 if(this.tabs && (panel = this.getPanel(panel))){
34006 this.tabs.unhideTab(panel.getEl().id);
34010 clearPanels : function(){
34011 while(this.panels.getCount() > 0){
34012 this.remove(this.panels.first());
34017 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34018 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34019 * @param {Boolean} preservePanel Overrides the config preservePanel option
34020 * @return {Roo.ContentPanel} The panel that was removed
34022 remove : function(panel, preservePanel)
34024 panel = this.getPanel(panel);
34029 this.fireEvent("beforeremove", this, panel, e);
34030 if(e.cancel === true){
34033 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34034 var panelId = panel.getId();
34035 this.panels.removeKey(panelId);
34037 document.body.appendChild(panel.getEl().dom);
34040 this.tabs.removeTab(panel.getEl().id);
34041 }else if (!preservePanel){
34042 this.bodyEl.dom.removeChild(panel.getEl().dom);
34044 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34045 var p = this.panels.first();
34046 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34047 tempEl.appendChild(p.getEl().dom);
34048 this.bodyEl.update("");
34049 this.bodyEl.dom.appendChild(p.getEl().dom);
34051 this.updateTitle(p.getTitle());
34053 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34054 this.setActivePanel(p);
34056 panel.setRegion(null);
34057 if(this.activePanel == panel){
34058 this.activePanel = null;
34060 if(this.config.autoDestroy !== false && preservePanel !== true){
34061 try{panel.destroy();}catch(e){}
34063 this.fireEvent("panelremoved", this, panel);
34068 * Returns the TabPanel component used by this region
34069 * @return {Roo.TabPanel}
34071 getTabs : function(){
34075 createTool : function(parentEl, className){
34076 var btn = Roo.DomHelper.append(parentEl, {
34078 cls: "x-layout-tools-button",
34081 cls: "roo-layout-tools-button-inner " + className,
34085 btn.addClassOnOver("roo-layout-tools-button-over");
34090 * Ext JS Library 1.1.1
34091 * Copyright(c) 2006-2007, Ext JS, LLC.
34093 * Originally Released Under LGPL - original licence link has changed is not relivant.
34096 * <script type="text/javascript">
34102 * @class Roo.SplitLayoutRegion
34103 * @extends Roo.LayoutRegion
34104 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34106 Roo.bootstrap.layout.Split = function(config){
34107 this.cursor = config.cursor;
34108 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34111 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34113 splitTip : "Drag to resize.",
34114 collapsibleSplitTip : "Drag to resize. Double click to hide.",
34115 useSplitTips : false,
34117 applyConfig : function(config){
34118 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34121 onRender : function(ctr,pos) {
34123 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34124 if(!this.config.split){
34129 var splitEl = Roo.DomHelper.append(ctr.dom, {
34131 id: this.el.id + "-split",
34132 cls: "roo-layout-split roo-layout-split-"+this.position,
34135 /** The SplitBar for this region
34136 * @type Roo.SplitBar */
34137 // does not exist yet...
34138 Roo.log([this.position, this.orientation]);
34140 this.split = new Roo.bootstrap.SplitBar({
34141 dragElement : splitEl,
34142 resizingElement: this.el,
34143 orientation : this.orientation
34146 this.split.on("moved", this.onSplitMove, this);
34147 this.split.useShim = this.config.useShim === true;
34148 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34149 if(this.useSplitTips){
34150 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34152 //if(config.collapsible){
34153 // this.split.el.on("dblclick", this.collapse, this);
34156 if(typeof this.config.minSize != "undefined"){
34157 this.split.minSize = this.config.minSize;
34159 if(typeof this.config.maxSize != "undefined"){
34160 this.split.maxSize = this.config.maxSize;
34162 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34163 this.hideSplitter();
34168 getHMaxSize : function(){
34169 var cmax = this.config.maxSize || 10000;
34170 var center = this.mgr.getRegion("center");
34171 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34174 getVMaxSize : function(){
34175 var cmax = this.config.maxSize || 10000;
34176 var center = this.mgr.getRegion("center");
34177 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34180 onSplitMove : function(split, newSize){
34181 this.fireEvent("resized", this, newSize);
34185 * Returns the {@link Roo.SplitBar} for this region.
34186 * @return {Roo.SplitBar}
34188 getSplitBar : function(){
34193 this.hideSplitter();
34194 Roo.bootstrap.layout.Split.superclass.hide.call(this);
34197 hideSplitter : function(){
34199 this.split.el.setLocation(-2000,-2000);
34200 this.split.el.hide();
34206 this.split.el.show();
34208 Roo.bootstrap.layout.Split.superclass.show.call(this);
34211 beforeSlide: function(){
34212 if(Roo.isGecko){// firefox overflow auto bug workaround
34213 this.bodyEl.clip();
34215 this.tabs.bodyEl.clip();
34217 if(this.activePanel){
34218 this.activePanel.getEl().clip();
34220 if(this.activePanel.beforeSlide){
34221 this.activePanel.beforeSlide();
34227 afterSlide : function(){
34228 if(Roo.isGecko){// firefox overflow auto bug workaround
34229 this.bodyEl.unclip();
34231 this.tabs.bodyEl.unclip();
34233 if(this.activePanel){
34234 this.activePanel.getEl().unclip();
34235 if(this.activePanel.afterSlide){
34236 this.activePanel.afterSlide();
34242 initAutoHide : function(){
34243 if(this.autoHide !== false){
34244 if(!this.autoHideHd){
34245 var st = new Roo.util.DelayedTask(this.slideIn, this);
34246 this.autoHideHd = {
34247 "mouseout": function(e){
34248 if(!e.within(this.el, true)){
34252 "mouseover" : function(e){
34258 this.el.on(this.autoHideHd);
34262 clearAutoHide : function(){
34263 if(this.autoHide !== false){
34264 this.el.un("mouseout", this.autoHideHd.mouseout);
34265 this.el.un("mouseover", this.autoHideHd.mouseover);
34269 clearMonitor : function(){
34270 Roo.get(document).un("click", this.slideInIf, this);
34273 // these names are backwards but not changed for compat
34274 slideOut : function(){
34275 if(this.isSlid || this.el.hasActiveFx()){
34278 this.isSlid = true;
34279 if(this.collapseBtn){
34280 this.collapseBtn.hide();
34282 this.closeBtnState = this.closeBtn.getStyle('display');
34283 this.closeBtn.hide();
34285 this.stickBtn.show();
34288 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34289 this.beforeSlide();
34290 this.el.setStyle("z-index", 10001);
34291 this.el.slideIn(this.getSlideAnchor(), {
34292 callback: function(){
34294 this.initAutoHide();
34295 Roo.get(document).on("click", this.slideInIf, this);
34296 this.fireEvent("slideshow", this);
34303 afterSlideIn : function(){
34304 this.clearAutoHide();
34305 this.isSlid = false;
34306 this.clearMonitor();
34307 this.el.setStyle("z-index", "");
34308 if(this.collapseBtn){
34309 this.collapseBtn.show();
34311 this.closeBtn.setStyle('display', this.closeBtnState);
34313 this.stickBtn.hide();
34315 this.fireEvent("slidehide", this);
34318 slideIn : function(cb){
34319 if(!this.isSlid || this.el.hasActiveFx()){
34323 this.isSlid = false;
34324 this.beforeSlide();
34325 this.el.slideOut(this.getSlideAnchor(), {
34326 callback: function(){
34327 this.el.setLeftTop(-10000, -10000);
34329 this.afterSlideIn();
34337 slideInIf : function(e){
34338 if(!e.within(this.el)){
34343 animateCollapse : function(){
34344 this.beforeSlide();
34345 this.el.setStyle("z-index", 20000);
34346 var anchor = this.getSlideAnchor();
34347 this.el.slideOut(anchor, {
34348 callback : function(){
34349 this.el.setStyle("z-index", "");
34350 this.collapsedEl.slideIn(anchor, {duration:.3});
34352 this.el.setLocation(-10000,-10000);
34354 this.fireEvent("collapsed", this);
34361 animateExpand : function(){
34362 this.beforeSlide();
34363 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34364 this.el.setStyle("z-index", 20000);
34365 this.collapsedEl.hide({
34368 this.el.slideIn(this.getSlideAnchor(), {
34369 callback : function(){
34370 this.el.setStyle("z-index", "");
34373 this.split.el.show();
34375 this.fireEvent("invalidated", this);
34376 this.fireEvent("expanded", this);
34404 getAnchor : function(){
34405 return this.anchors[this.position];
34408 getCollapseAnchor : function(){
34409 return this.canchors[this.position];
34412 getSlideAnchor : function(){
34413 return this.sanchors[this.position];
34416 getAlignAdj : function(){
34417 var cm = this.cmargins;
34418 switch(this.position){
34434 getExpandAdj : function(){
34435 var c = this.collapsedEl, cm = this.cmargins;
34436 switch(this.position){
34438 return [-(cm.right+c.getWidth()+cm.left), 0];
34441 return [cm.right+c.getWidth()+cm.left, 0];
34444 return [0, -(cm.top+cm.bottom+c.getHeight())];
34447 return [0, cm.top+cm.bottom+c.getHeight()];
34453 * Ext JS Library 1.1.1
34454 * Copyright(c) 2006-2007, Ext JS, LLC.
34456 * Originally Released Under LGPL - original licence link has changed is not relivant.
34459 * <script type="text/javascript">
34462 * These classes are private internal classes
34464 Roo.bootstrap.layout.Center = function(config){
34465 config.region = "center";
34466 Roo.bootstrap.layout.Region.call(this, config);
34467 this.visible = true;
34468 this.minWidth = config.minWidth || 20;
34469 this.minHeight = config.minHeight || 20;
34472 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34474 // center panel can't be hidden
34478 // center panel can't be hidden
34481 getMinWidth: function(){
34482 return this.minWidth;
34485 getMinHeight: function(){
34486 return this.minHeight;
34499 Roo.bootstrap.layout.North = function(config)
34501 config.region = 'north';
34502 config.cursor = 'n-resize';
34504 Roo.bootstrap.layout.Split.call(this, config);
34508 this.split.placement = Roo.bootstrap.SplitBar.TOP;
34509 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34510 this.split.el.addClass("roo-layout-split-v");
34512 var size = config.initialSize || config.height;
34513 if(typeof size != "undefined"){
34514 this.el.setHeight(size);
34517 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34519 orientation: Roo.bootstrap.SplitBar.VERTICAL,
34523 getBox : function(){
34524 if(this.collapsed){
34525 return this.collapsedEl.getBox();
34527 var box = this.el.getBox();
34529 box.height += this.split.el.getHeight();
34534 updateBox : function(box){
34535 if(this.split && !this.collapsed){
34536 box.height -= this.split.el.getHeight();
34537 this.split.el.setLeft(box.x);
34538 this.split.el.setTop(box.y+box.height);
34539 this.split.el.setWidth(box.width);
34541 if(this.collapsed){
34542 this.updateBody(box.width, null);
34544 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34552 Roo.bootstrap.layout.South = function(config){
34553 config.region = 'south';
34554 config.cursor = 's-resize';
34555 Roo.bootstrap.layout.Split.call(this, config);
34557 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34558 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34559 this.split.el.addClass("roo-layout-split-v");
34561 var size = config.initialSize || config.height;
34562 if(typeof size != "undefined"){
34563 this.el.setHeight(size);
34567 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34568 orientation: Roo.bootstrap.SplitBar.VERTICAL,
34569 getBox : function(){
34570 if(this.collapsed){
34571 return this.collapsedEl.getBox();
34573 var box = this.el.getBox();
34575 var sh = this.split.el.getHeight();
34582 updateBox : function(box){
34583 if(this.split && !this.collapsed){
34584 var sh = this.split.el.getHeight();
34587 this.split.el.setLeft(box.x);
34588 this.split.el.setTop(box.y-sh);
34589 this.split.el.setWidth(box.width);
34591 if(this.collapsed){
34592 this.updateBody(box.width, null);
34594 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34598 Roo.bootstrap.layout.East = function(config){
34599 config.region = "east";
34600 config.cursor = "e-resize";
34601 Roo.bootstrap.layout.Split.call(this, config);
34603 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34604 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34605 this.split.el.addClass("roo-layout-split-h");
34607 var size = config.initialSize || config.width;
34608 if(typeof size != "undefined"){
34609 this.el.setWidth(size);
34612 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34613 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34614 getBox : function(){
34615 if(this.collapsed){
34616 return this.collapsedEl.getBox();
34618 var box = this.el.getBox();
34620 var sw = this.split.el.getWidth();
34627 updateBox : function(box){
34628 if(this.split && !this.collapsed){
34629 var sw = this.split.el.getWidth();
34631 this.split.el.setLeft(box.x);
34632 this.split.el.setTop(box.y);
34633 this.split.el.setHeight(box.height);
34636 if(this.collapsed){
34637 this.updateBody(null, box.height);
34639 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34643 Roo.bootstrap.layout.West = function(config){
34644 config.region = "west";
34645 config.cursor = "w-resize";
34647 Roo.bootstrap.layout.Split.call(this, config);
34649 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34650 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34651 this.split.el.addClass("roo-layout-split-h");
34655 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34656 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34658 onRender: function(ctr, pos)
34660 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34661 var size = this.config.initialSize || this.config.width;
34662 if(typeof size != "undefined"){
34663 this.el.setWidth(size);
34667 getBox : function(){
34668 if(this.collapsed){
34669 return this.collapsedEl.getBox();
34671 var box = this.el.getBox();
34673 box.width += this.split.el.getWidth();
34678 updateBox : function(box){
34679 if(this.split && !this.collapsed){
34680 var sw = this.split.el.getWidth();
34682 this.split.el.setLeft(box.x+box.width);
34683 this.split.el.setTop(box.y);
34684 this.split.el.setHeight(box.height);
34686 if(this.collapsed){
34687 this.updateBody(null, box.height);
34689 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34692 Roo.namespace("Roo.bootstrap.panel");/*
34694 * Ext JS Library 1.1.1
34695 * Copyright(c) 2006-2007, Ext JS, LLC.
34697 * Originally Released Under LGPL - original licence link has changed is not relivant.
34700 * <script type="text/javascript">
34703 * @class Roo.ContentPanel
34704 * @extends Roo.util.Observable
34705 * A basic ContentPanel element.
34706 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
34707 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
34708 * @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
34709 * @cfg {Boolean} closable True if the panel can be closed/removed
34710 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
34711 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34712 * @cfg {Toolbar} toolbar A toolbar for this panel
34713 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
34714 * @cfg {String} title The title for this panel
34715 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34716 * @cfg {String} url Calls {@link #setUrl} with this value
34717 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34718 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
34719 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
34720 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
34721 * @cfg {Boolean} badges render the badges
34724 * Create a new ContentPanel.
34725 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34726 * @param {String/Object} config A string to set only the title or a config object
34727 * @param {String} content (optional) Set the HTML content for this panel
34728 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34730 Roo.bootstrap.panel.Content = function( config){
34732 this.tpl = config.tpl || false;
34734 var el = config.el;
34735 var content = config.content;
34737 if(config.autoCreate){ // xtype is available if this is called from factory
34740 this.el = Roo.get(el);
34741 if(!this.el && config && config.autoCreate){
34742 if(typeof config.autoCreate == "object"){
34743 if(!config.autoCreate.id){
34744 config.autoCreate.id = config.id||el;
34746 this.el = Roo.DomHelper.append(document.body,
34747 config.autoCreate, true);
34749 var elcfg = { tag: "div",
34750 cls: "roo-layout-inactive-content",
34754 elcfg.html = config.html;
34758 this.el = Roo.DomHelper.append(document.body, elcfg , true);
34761 this.closable = false;
34762 this.loaded = false;
34763 this.active = false;
34766 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
34768 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
34770 this.wrapEl = this.el; //this.el.wrap();
34772 if (config.toolbar.items) {
34773 ti = config.toolbar.items ;
34774 delete config.toolbar.items ;
34778 this.toolbar.render(this.wrapEl, 'before');
34779 for(var i =0;i < ti.length;i++) {
34780 // Roo.log(['add child', items[i]]);
34781 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34783 this.toolbar.items = nitems;
34784 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34785 delete config.toolbar;
34789 // xtype created footer. - not sure if will work as we normally have to render first..
34790 if (this.footer && !this.footer.el && this.footer.xtype) {
34791 if (!this.wrapEl) {
34792 this.wrapEl = this.el.wrap();
34795 this.footer.container = this.wrapEl.createChild();
34797 this.footer = Roo.factory(this.footer, Roo);
34802 if(typeof config == "string"){
34803 this.title = config;
34805 Roo.apply(this, config);
34809 this.resizeEl = Roo.get(this.resizeEl, true);
34811 this.resizeEl = this.el;
34813 // handle view.xtype
34821 * Fires when this panel is activated.
34822 * @param {Roo.ContentPanel} this
34826 * @event deactivate
34827 * Fires when this panel is activated.
34828 * @param {Roo.ContentPanel} this
34830 "deactivate" : true,
34834 * Fires when this panel is resized if fitToFrame is true.
34835 * @param {Roo.ContentPanel} this
34836 * @param {Number} width The width after any component adjustments
34837 * @param {Number} height The height after any component adjustments
34843 * Fires when this tab is created
34844 * @param {Roo.ContentPanel} this
34855 if(this.autoScroll){
34856 this.resizeEl.setStyle("overflow", "auto");
34858 // fix randome scrolling
34859 //this.el.on('scroll', function() {
34860 // Roo.log('fix random scolling');
34861 // this.scrollTo('top',0);
34864 content = content || this.content;
34866 this.setContent(content);
34868 if(config && config.url){
34869 this.setUrl(this.url, this.params, this.loadOnce);
34874 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34876 if (this.view && typeof(this.view.xtype) != 'undefined') {
34877 this.view.el = this.el.appendChild(document.createElement("div"));
34878 this.view = Roo.factory(this.view);
34879 this.view.render && this.view.render(false, '');
34883 this.fireEvent('render', this);
34886 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34890 setRegion : function(region){
34891 this.region = region;
34892 this.setActiveClass(region && !this.background);
34896 setActiveClass: function(state)
34899 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
34900 this.el.setStyle('position','relative');
34902 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
34903 this.el.setStyle('position', 'absolute');
34908 * Returns the toolbar for this Panel if one was configured.
34909 * @return {Roo.Toolbar}
34911 getToolbar : function(){
34912 return this.toolbar;
34915 setActiveState : function(active)
34917 this.active = active;
34918 this.setActiveClass(active);
34920 this.fireEvent("deactivate", this);
34922 this.fireEvent("activate", this);
34926 * Updates this panel's element
34927 * @param {String} content The new content
34928 * @param {Boolean} loadScripts (optional) true to look for and process scripts
34930 setContent : function(content, loadScripts){
34931 this.el.update(content, loadScripts);
34934 ignoreResize : function(w, h){
34935 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34938 this.lastSize = {width: w, height: h};
34943 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34944 * @return {Roo.UpdateManager} The UpdateManager
34946 getUpdateManager : function(){
34947 return this.el.getUpdateManager();
34950 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34951 * @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:
34954 url: "your-url.php",
34955 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34956 callback: yourFunction,
34957 scope: yourObject, //(optional scope)
34960 text: "Loading...",
34965 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34966 * 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.
34967 * @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}
34968 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34969 * @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.
34970 * @return {Roo.ContentPanel} this
34973 var um = this.el.getUpdateManager();
34974 um.update.apply(um, arguments);
34980 * 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.
34981 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34982 * @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)
34983 * @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)
34984 * @return {Roo.UpdateManager} The UpdateManager
34986 setUrl : function(url, params, loadOnce){
34987 if(this.refreshDelegate){
34988 this.removeListener("activate", this.refreshDelegate);
34990 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34991 this.on("activate", this.refreshDelegate);
34992 return this.el.getUpdateManager();
34995 _handleRefresh : function(url, params, loadOnce){
34996 if(!loadOnce || !this.loaded){
34997 var updater = this.el.getUpdateManager();
34998 updater.update(url, params, this._setLoaded.createDelegate(this));
35002 _setLoaded : function(){
35003 this.loaded = true;
35007 * Returns this panel's id
35010 getId : function(){
35015 * Returns this panel's element - used by regiosn to add.
35016 * @return {Roo.Element}
35018 getEl : function(){
35019 return this.wrapEl || this.el;
35024 adjustForComponents : function(width, height)
35026 //Roo.log('adjustForComponents ');
35027 if(this.resizeEl != this.el){
35028 width -= this.el.getFrameWidth('lr');
35029 height -= this.el.getFrameWidth('tb');
35032 var te = this.toolbar.getEl();
35033 height -= te.getHeight();
35034 te.setWidth(width);
35037 var te = this.footer.getEl();
35038 Roo.log("footer:" + te.getHeight());
35040 height -= te.getHeight();
35041 te.setWidth(width);
35045 if(this.adjustments){
35046 width += this.adjustments[0];
35047 height += this.adjustments[1];
35049 return {"width": width, "height": height};
35052 setSize : function(width, height){
35053 if(this.fitToFrame && !this.ignoreResize(width, height)){
35054 if(this.fitContainer && this.resizeEl != this.el){
35055 this.el.setSize(width, height);
35057 var size = this.adjustForComponents(width, height);
35058 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35059 this.fireEvent('resize', this, size.width, size.height);
35064 * Returns this panel's title
35067 getTitle : function(){
35072 * Set this panel's title
35073 * @param {String} title
35075 setTitle : function(title){
35076 this.title = title;
35078 this.region.updatePanelTitle(this, title);
35083 * Returns true is this panel was configured to be closable
35084 * @return {Boolean}
35086 isClosable : function(){
35087 return this.closable;
35090 beforeSlide : function(){
35092 this.resizeEl.clip();
35095 afterSlide : function(){
35097 this.resizeEl.unclip();
35101 * Force a content refresh from the URL specified in the {@link #setUrl} method.
35102 * Will fail silently if the {@link #setUrl} method has not been called.
35103 * This does not activate the panel, just updates its content.
35105 refresh : function(){
35106 if(this.refreshDelegate){
35107 this.loaded = false;
35108 this.refreshDelegate();
35113 * Destroys this panel
35115 destroy : function(){
35116 this.el.removeAllListeners();
35117 var tempEl = document.createElement("span");
35118 tempEl.appendChild(this.el.dom);
35119 tempEl.innerHTML = "";
35125 * form - if the content panel contains a form - this is a reference to it.
35126 * @type {Roo.form.Form}
35130 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35131 * This contains a reference to it.
35137 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35147 * @param {Object} cfg Xtype definition of item to add.
35151 getChildContainer: function () {
35152 return this.getEl();
35157 var ret = new Roo.factory(cfg);
35162 if (cfg.xtype.match(/^Form$/)) {
35165 //if (this.footer) {
35166 // el = this.footer.container.insertSibling(false, 'before');
35168 el = this.el.createChild();
35171 this.form = new Roo.form.Form(cfg);
35174 if ( this.form.allItems.length) {
35175 this.form.render(el.dom);
35179 // should only have one of theses..
35180 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35181 // views.. should not be just added - used named prop 'view''
35183 cfg.el = this.el.appendChild(document.createElement("div"));
35186 var ret = new Roo.factory(cfg);
35188 ret.render && ret.render(false, ''); // render blank..
35198 * @class Roo.bootstrap.panel.Grid
35199 * @extends Roo.bootstrap.panel.Content
35201 * Create a new GridPanel.
35202 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
35203 * @param {Object} config A the config object
35209 Roo.bootstrap.panel.Grid = function(config)
35213 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35214 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
35216 config.el = this.wrapper;
35217 //this.el = this.wrapper;
35219 if (config.container) {
35220 // ctor'ed from a Border/panel.grid
35223 this.wrapper.setStyle("overflow", "hidden");
35224 this.wrapper.addClass('roo-grid-container');
35229 if(config.toolbar){
35230 var tool_el = this.wrapper.createChild();
35231 this.toolbar = Roo.factory(config.toolbar);
35233 if (config.toolbar.items) {
35234 ti = config.toolbar.items ;
35235 delete config.toolbar.items ;
35239 this.toolbar.render(tool_el);
35240 for(var i =0;i < ti.length;i++) {
35241 // Roo.log(['add child', items[i]]);
35242 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35244 this.toolbar.items = nitems;
35246 delete config.toolbar;
35249 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
35250 config.grid.scrollBody = true;;
35251 config.grid.monitorWindowResize = false; // turn off autosizing
35252 config.grid.autoHeight = false;
35253 config.grid.autoWidth = false;
35255 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
35257 if (config.background) {
35258 // render grid on panel activation (if panel background)
35259 this.on('activate', function(gp) {
35260 if (!gp.grid.rendered) {
35261 gp.grid.render(this.wrapper);
35262 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
35267 this.grid.render(this.wrapper);
35268 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
35271 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
35272 // ??? needed ??? config.el = this.wrapper;
35277 // xtype created footer. - not sure if will work as we normally have to render first..
35278 if (this.footer && !this.footer.el && this.footer.xtype) {
35280 var ctr = this.grid.getView().getFooterPanel(true);
35281 this.footer.dataSource = this.grid.dataSource;
35282 this.footer = Roo.factory(this.footer, Roo);
35283 this.footer.render(ctr);
35293 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
35294 getId : function(){
35295 return this.grid.id;
35299 * Returns the grid for this panel
35300 * @return {Roo.bootstrap.Table}
35302 getGrid : function(){
35306 setSize : function(width, height){
35307 if(!this.ignoreResize(width, height)){
35308 var grid = this.grid;
35309 var size = this.adjustForComponents(width, height);
35310 var gridel = grid.getGridEl();
35311 gridel.setSize(size.width, size.height);
35313 var thd = grid.getGridEl().select('thead',true).first();
35314 var tbd = grid.getGridEl().select('tbody', true).first();
35316 tbd.setSize(width, height - thd.getHeight());
35325 beforeSlide : function(){
35326 this.grid.getView().scroller.clip();
35329 afterSlide : function(){
35330 this.grid.getView().scroller.unclip();
35333 destroy : function(){
35334 this.grid.destroy();
35336 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
35341 * @class Roo.bootstrap.panel.Nest
35342 * @extends Roo.bootstrap.panel.Content
35344 * Create a new Panel, that can contain a layout.Border.
35347 * @param {Roo.BorderLayout} layout The layout for this panel
35348 * @param {String/Object} config A string to set only the title or a config object
35350 Roo.bootstrap.panel.Nest = function(config)
35352 // construct with only one argument..
35353 /* FIXME - implement nicer consturctors
35354 if (layout.layout) {
35356 layout = config.layout;
35357 delete config.layout;
35359 if (layout.xtype && !layout.getEl) {
35360 // then layout needs constructing..
35361 layout = Roo.factory(layout, Roo);
35365 config.el = config.layout.getEl();
35367 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35369 config.layout.monitorWindowResize = false; // turn off autosizing
35370 this.layout = config.layout;
35371 this.layout.getEl().addClass("roo-layout-nested-layout");
35378 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35380 setSize : function(width, height){
35381 if(!this.ignoreResize(width, height)){
35382 var size = this.adjustForComponents(width, height);
35383 var el = this.layout.getEl();
35384 if (size.height < 1) {
35385 el.setWidth(size.width);
35387 el.setSize(size.width, size.height);
35389 var touch = el.dom.offsetWidth;
35390 this.layout.layout();
35391 // ie requires a double layout on the first pass
35392 if(Roo.isIE && !this.initialized){
35393 this.initialized = true;
35394 this.layout.layout();
35399 // activate all subpanels if not currently active..
35401 setActiveState : function(active){
35402 this.active = active;
35403 this.setActiveClass(active);
35406 this.fireEvent("deactivate", this);
35410 this.fireEvent("activate", this);
35411 // not sure if this should happen before or after..
35412 if (!this.layout) {
35413 return; // should not happen..
35416 for (var r in this.layout.regions) {
35417 reg = this.layout.getRegion(r);
35418 if (reg.getActivePanel()) {
35419 //reg.showPanel(reg.getActivePanel()); // force it to activate..
35420 reg.setActivePanel(reg.getActivePanel());
35423 if (!reg.panels.length) {
35426 reg.showPanel(reg.getPanel(0));
35435 * Returns the nested BorderLayout for this panel
35436 * @return {Roo.BorderLayout}
35438 getLayout : function(){
35439 return this.layout;
35443 * Adds a xtype elements to the layout of the nested panel
35447 xtype : 'ContentPanel',
35454 xtype : 'NestedLayoutPanel',
35460 items : [ ... list of content panels or nested layout panels.. ]
35464 * @param {Object} cfg Xtype definition of item to add.
35466 addxtype : function(cfg) {
35467 return this.layout.addxtype(cfg);
35472 * Ext JS Library 1.1.1
35473 * Copyright(c) 2006-2007, Ext JS, LLC.
35475 * Originally Released Under LGPL - original licence link has changed is not relivant.
35478 * <script type="text/javascript">
35481 * @class Roo.TabPanel
35482 * @extends Roo.util.Observable
35483 * A lightweight tab container.
35487 // basic tabs 1, built from existing content
35488 var tabs = new Roo.TabPanel("tabs1");
35489 tabs.addTab("script", "View Script");
35490 tabs.addTab("markup", "View Markup");
35491 tabs.activate("script");
35493 // more advanced tabs, built from javascript
35494 var jtabs = new Roo.TabPanel("jtabs");
35495 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35497 // set up the UpdateManager
35498 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35499 var updater = tab2.getUpdateManager();
35500 updater.setDefaultUrl("ajax1.htm");
35501 tab2.on('activate', updater.refresh, updater, true);
35503 // Use setUrl for Ajax loading
35504 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35505 tab3.setUrl("ajax2.htm", null, true);
35508 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35511 jtabs.activate("jtabs-1");
35514 * Create a new TabPanel.
35515 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35516 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35518 Roo.bootstrap.panel.Tabs = function(config){
35520 * The container element for this TabPanel.
35521 * @type Roo.Element
35523 this.el = Roo.get(config.el);
35526 if(typeof config == "boolean"){
35527 this.tabPosition = config ? "bottom" : "top";
35529 Roo.apply(this, config);
35533 if(this.tabPosition == "bottom"){
35534 this.bodyEl = Roo.get(this.createBody(this.el.dom));
35535 this.el.addClass("roo-tabs-bottom");
35537 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35538 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35539 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35541 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35543 if(this.tabPosition != "bottom"){
35544 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35545 * @type Roo.Element
35547 this.bodyEl = Roo.get(this.createBody(this.el.dom));
35548 this.el.addClass("roo-tabs-top");
35552 this.bodyEl.setStyle("position", "relative");
35554 this.active = null;
35555 this.activateDelegate = this.activate.createDelegate(this);
35560 * Fires when the active tab changes
35561 * @param {Roo.TabPanel} this
35562 * @param {Roo.TabPanelItem} activePanel The new active tab
35566 * @event beforetabchange
35567 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35568 * @param {Roo.TabPanel} this
35569 * @param {Object} e Set cancel to true on this object to cancel the tab change
35570 * @param {Roo.TabPanelItem} tab The tab being changed to
35572 "beforetabchange" : true
35575 Roo.EventManager.onWindowResize(this.onResize, this);
35576 this.cpad = this.el.getPadding("lr");
35577 this.hiddenCount = 0;
35580 // toolbar on the tabbar support...
35581 if (this.toolbar) {
35582 alert("no toolbar support yet");
35583 this.toolbar = false;
35585 var tcfg = this.toolbar;
35586 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
35587 this.toolbar = new Roo.Toolbar(tcfg);
35588 if (Roo.isSafari) {
35589 var tbl = tcfg.container.child('table', true);
35590 tbl.setAttribute('width', '100%');
35598 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35601 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35603 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35605 tabPosition : "top",
35607 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35609 currentTabWidth : 0,
35611 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35615 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35619 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35621 preferredTabWidth : 175,
35623 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35625 resizeTabs : false,
35627 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35629 monitorResize : true,
35631 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
35636 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35637 * @param {String} id The id of the div to use <b>or create</b>
35638 * @param {String} text The text for the tab
35639 * @param {String} content (optional) Content to put in the TabPanelItem body
35640 * @param {Boolean} closable (optional) True to create a close icon on the tab
35641 * @return {Roo.TabPanelItem} The created TabPanelItem
35643 addTab : function(id, text, content, closable, tpl)
35645 var item = new Roo.bootstrap.panel.TabItem({
35649 closable : closable,
35652 this.addTabItem(item);
35654 item.setContent(content);
35660 * Returns the {@link Roo.TabPanelItem} with the specified id/index
35661 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35662 * @return {Roo.TabPanelItem}
35664 getTab : function(id){
35665 return this.items[id];
35669 * Hides the {@link Roo.TabPanelItem} with the specified id/index
35670 * @param {String/Number} id The id or index of the TabPanelItem to hide.
35672 hideTab : function(id){
35673 var t = this.items[id];
35676 this.hiddenCount++;
35677 this.autoSizeTabs();
35682 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35683 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35685 unhideTab : function(id){
35686 var t = this.items[id];
35688 t.setHidden(false);
35689 this.hiddenCount--;
35690 this.autoSizeTabs();
35695 * Adds an existing {@link Roo.TabPanelItem}.
35696 * @param {Roo.TabPanelItem} item The TabPanelItem to add
35698 addTabItem : function(item){
35699 this.items[item.id] = item;
35700 this.items.push(item);
35701 // if(this.resizeTabs){
35702 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35703 // this.autoSizeTabs();
35705 // item.autoSize();
35710 * Removes a {@link Roo.TabPanelItem}.
35711 * @param {String/Number} id The id or index of the TabPanelItem to remove.
35713 removeTab : function(id){
35714 var items = this.items;
35715 var tab = items[id];
35716 if(!tab) { return; }
35717 var index = items.indexOf(tab);
35718 if(this.active == tab && items.length > 1){
35719 var newTab = this.getNextAvailable(index);
35724 this.stripEl.dom.removeChild(tab.pnode.dom);
35725 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
35726 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
35728 items.splice(index, 1);
35729 delete this.items[tab.id];
35730 tab.fireEvent("close", tab);
35731 tab.purgeListeners();
35732 this.autoSizeTabs();
35735 getNextAvailable : function(start){
35736 var items = this.items;
35738 // look for a next tab that will slide over to
35739 // replace the one being removed
35740 while(index < items.length){
35741 var item = items[++index];
35742 if(item && !item.isHidden()){
35746 // if one isn't found select the previous tab (on the left)
35749 var item = items[--index];
35750 if(item && !item.isHidden()){
35758 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
35759 * @param {String/Number} id The id or index of the TabPanelItem to disable.
35761 disableTab : function(id){
35762 var tab = this.items[id];
35763 if(tab && this.active != tab){
35769 * Enables a {@link Roo.TabPanelItem} that is disabled.
35770 * @param {String/Number} id The id or index of the TabPanelItem to enable.
35772 enableTab : function(id){
35773 var tab = this.items[id];
35778 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
35779 * @param {String/Number} id The id or index of the TabPanelItem to activate.
35780 * @return {Roo.TabPanelItem} The TabPanelItem.
35782 activate : function(id){
35783 var tab = this.items[id];
35787 if(tab == this.active || tab.disabled){
35791 this.fireEvent("beforetabchange", this, e, tab);
35792 if(e.cancel !== true && !tab.disabled){
35794 this.active.hide();
35796 this.active = this.items[id];
35797 this.active.show();
35798 this.fireEvent("tabchange", this, this.active);
35804 * Gets the active {@link Roo.TabPanelItem}.
35805 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35807 getActiveTab : function(){
35808 return this.active;
35812 * Updates the tab body element to fit the height of the container element
35813 * for overflow scrolling
35814 * @param {Number} targetHeight (optional) Override the starting height from the elements height
35816 syncHeight : function(targetHeight){
35817 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35818 var bm = this.bodyEl.getMargins();
35819 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35820 this.bodyEl.setHeight(newHeight);
35824 onResize : function(){
35825 if(this.monitorResize){
35826 this.autoSizeTabs();
35831 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35833 beginUpdate : function(){
35834 this.updating = true;
35838 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35840 endUpdate : function(){
35841 this.updating = false;
35842 this.autoSizeTabs();
35846 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35848 autoSizeTabs : function(){
35849 var count = this.items.length;
35850 var vcount = count - this.hiddenCount;
35851 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35854 var w = Math.max(this.el.getWidth() - this.cpad, 10);
35855 var availWidth = Math.floor(w / vcount);
35856 var b = this.stripBody;
35857 if(b.getWidth() > w){
35858 var tabs = this.items;
35859 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35860 if(availWidth < this.minTabWidth){
35861 /*if(!this.sleft){ // incomplete scrolling code
35862 this.createScrollButtons();
35865 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35868 if(this.currentTabWidth < this.preferredTabWidth){
35869 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35875 * Returns the number of tabs in this TabPanel.
35878 getCount : function(){
35879 return this.items.length;
35883 * Resizes all the tabs to the passed width
35884 * @param {Number} The new width
35886 setTabWidth : function(width){
35887 this.currentTabWidth = width;
35888 for(var i = 0, len = this.items.length; i < len; i++) {
35889 if(!this.items[i].isHidden()) {
35890 this.items[i].setWidth(width);
35896 * Destroys this TabPanel
35897 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
35899 destroy : function(removeEl){
35900 Roo.EventManager.removeResizeListener(this.onResize, this);
35901 for(var i = 0, len = this.items.length; i < len; i++){
35902 this.items[i].purgeListeners();
35904 if(removeEl === true){
35905 this.el.update("");
35910 createStrip : function(container)
35912 var strip = document.createElement("nav");
35913 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
35914 container.appendChild(strip);
35918 createStripList : function(strip)
35920 // div wrapper for retard IE
35921 // returns the "tr" element.
35922 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
35923 //'<div class="x-tabs-strip-wrap">'+
35924 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
35925 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
35926 return strip.firstChild; //.firstChild.firstChild.firstChild;
35928 createBody : function(container)
35930 var body = document.createElement("div");
35931 Roo.id(body, "tab-body");
35932 //Roo.fly(body).addClass("x-tabs-body");
35933 Roo.fly(body).addClass("tab-content");
35934 container.appendChild(body);
35937 createItemBody :function(bodyEl, id){
35938 var body = Roo.getDom(id);
35940 body = document.createElement("div");
35943 //Roo.fly(body).addClass("x-tabs-item-body");
35944 Roo.fly(body).addClass("tab-pane");
35945 bodyEl.insertBefore(body, bodyEl.firstChild);
35949 createStripElements : function(stripEl, text, closable, tpl)
35951 var td = document.createElement("li"); // was td..
35954 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
35957 stripEl.appendChild(td);
35959 td.className = "x-tabs-closable";
35960 if(!this.closeTpl){
35961 this.closeTpl = new Roo.Template(
35962 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35963 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
35964 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
35967 var el = this.closeTpl.overwrite(td, {"text": text});
35968 var close = el.getElementsByTagName("div")[0];
35969 var inner = el.getElementsByTagName("em")[0];
35970 return {"el": el, "close": close, "inner": inner};
35973 // not sure what this is..
35974 // if(!this.tabTpl){
35975 //this.tabTpl = new Roo.Template(
35976 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35977 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
35979 // this.tabTpl = new Roo.Template(
35980 // '<a href="#">' +
35981 // '<span unselectable="on"' +
35982 // (this.disableTooltips ? '' : ' title="{text}"') +
35983 // ' >{text}</span></a>'
35989 var template = tpl || this.tabTpl || false;
35993 template = new Roo.Template(
35995 '<span unselectable="on"' +
35996 (this.disableTooltips ? '' : ' title="{text}"') +
35997 ' >{text}</span></a>'
36001 switch (typeof(template)) {
36005 template = new Roo.Template(template);
36011 var el = template.overwrite(td, {"text": text});
36013 var inner = el.getElementsByTagName("span")[0];
36015 return {"el": el, "inner": inner};
36023 * @class Roo.TabPanelItem
36024 * @extends Roo.util.Observable
36025 * Represents an individual item (tab plus body) in a TabPanel.
36026 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36027 * @param {String} id The id of this TabPanelItem
36028 * @param {String} text The text for the tab of this TabPanelItem
36029 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36031 Roo.bootstrap.panel.TabItem = function(config){
36033 * The {@link Roo.TabPanel} this TabPanelItem belongs to
36034 * @type Roo.TabPanel
36036 this.tabPanel = config.panel;
36038 * The id for this TabPanelItem
36041 this.id = config.id;
36043 this.disabled = false;
36045 this.text = config.text;
36047 this.loaded = false;
36048 this.closable = config.closable;
36051 * The body element for this TabPanelItem.
36052 * @type Roo.Element
36054 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36055 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36056 this.bodyEl.setStyle("display", "block");
36057 this.bodyEl.setStyle("zoom", "1");
36058 //this.hideAction();
36060 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36062 this.el = Roo.get(els.el);
36063 this.inner = Roo.get(els.inner, true);
36064 this.textEl = Roo.get(this.el.dom.firstChild, true);
36065 this.pnode = Roo.get(els.el.parentNode, true);
36066 this.el.on("mousedown", this.onTabMouseDown, this);
36067 this.el.on("click", this.onTabClick, this);
36069 if(config.closable){
36070 var c = Roo.get(els.close, true);
36071 c.dom.title = this.closeText;
36072 c.addClassOnOver("close-over");
36073 c.on("click", this.closeClick, this);
36079 * Fires when this tab becomes the active tab.
36080 * @param {Roo.TabPanel} tabPanel The parent TabPanel
36081 * @param {Roo.TabPanelItem} this
36085 * @event beforeclose
36086 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36087 * @param {Roo.TabPanelItem} this
36088 * @param {Object} e Set cancel to true on this object to cancel the close.
36090 "beforeclose": true,
36093 * Fires when this tab is closed.
36094 * @param {Roo.TabPanelItem} this
36098 * @event deactivate
36099 * Fires when this tab is no longer the active tab.
36100 * @param {Roo.TabPanel} tabPanel The parent TabPanel
36101 * @param {Roo.TabPanelItem} this
36103 "deactivate" : true
36105 this.hidden = false;
36107 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36110 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36112 purgeListeners : function(){
36113 Roo.util.Observable.prototype.purgeListeners.call(this);
36114 this.el.removeAllListeners();
36117 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36120 this.pnode.addClass("active");
36123 this.tabPanel.stripWrap.repaint();
36125 this.fireEvent("activate", this.tabPanel, this);
36129 * Returns true if this tab is the active tab.
36130 * @return {Boolean}
36132 isActive : function(){
36133 return this.tabPanel.getActiveTab() == this;
36137 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36140 this.pnode.removeClass("active");
36142 this.fireEvent("deactivate", this.tabPanel, this);
36145 hideAction : function(){
36146 this.bodyEl.hide();
36147 this.bodyEl.setStyle("position", "absolute");
36148 this.bodyEl.setLeft("-20000px");
36149 this.bodyEl.setTop("-20000px");
36152 showAction : function(){
36153 this.bodyEl.setStyle("position", "relative");
36154 this.bodyEl.setTop("");
36155 this.bodyEl.setLeft("");
36156 this.bodyEl.show();
36160 * Set the tooltip for the tab.
36161 * @param {String} tooltip The tab's tooltip
36163 setTooltip : function(text){
36164 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36165 this.textEl.dom.qtip = text;
36166 this.textEl.dom.removeAttribute('title');
36168 this.textEl.dom.title = text;
36172 onTabClick : function(e){
36173 e.preventDefault();
36174 this.tabPanel.activate(this.id);
36177 onTabMouseDown : function(e){
36178 e.preventDefault();
36179 this.tabPanel.activate(this.id);
36182 getWidth : function(){
36183 return this.inner.getWidth();
36186 setWidth : function(width){
36187 var iwidth = width - this.pnode.getPadding("lr");
36188 this.inner.setWidth(iwidth);
36189 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
36190 this.pnode.setWidth(width);
36194 * Show or hide the tab
36195 * @param {Boolean} hidden True to hide or false to show.
36197 setHidden : function(hidden){
36198 this.hidden = hidden;
36199 this.pnode.setStyle("display", hidden ? "none" : "");
36203 * Returns true if this tab is "hidden"
36204 * @return {Boolean}
36206 isHidden : function(){
36207 return this.hidden;
36211 * Returns the text for this tab
36214 getText : function(){
36218 autoSize : function(){
36219 //this.el.beginMeasure();
36220 this.textEl.setWidth(1);
36222 * #2804 [new] Tabs in Roojs
36223 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
36225 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
36226 //this.el.endMeasure();
36230 * Sets the text for the tab (Note: this also sets the tooltip text)
36231 * @param {String} text The tab's text and tooltip
36233 setText : function(text){
36235 this.textEl.update(text);
36236 this.setTooltip(text);
36237 //if(!this.tabPanel.resizeTabs){
36238 // this.autoSize();
36242 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
36244 activate : function(){
36245 this.tabPanel.activate(this.id);
36249 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
36251 disable : function(){
36252 if(this.tabPanel.active != this){
36253 this.disabled = true;
36254 this.pnode.addClass("disabled");
36259 * Enables this TabPanelItem if it was previously disabled.
36261 enable : function(){
36262 this.disabled = false;
36263 this.pnode.removeClass("disabled");
36267 * Sets the content for this TabPanelItem.
36268 * @param {String} content The content
36269 * @param {Boolean} loadScripts true to look for and load scripts
36271 setContent : function(content, loadScripts){
36272 this.bodyEl.update(content, loadScripts);
36276 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
36277 * @return {Roo.UpdateManager} The UpdateManager
36279 getUpdateManager : function(){
36280 return this.bodyEl.getUpdateManager();
36284 * Set a URL to be used to load the content for this TabPanelItem.
36285 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
36286 * @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)
36287 * @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)
36288 * @return {Roo.UpdateManager} The UpdateManager
36290 setUrl : function(url, params, loadOnce){
36291 if(this.refreshDelegate){
36292 this.un('activate', this.refreshDelegate);
36294 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36295 this.on("activate", this.refreshDelegate);
36296 return this.bodyEl.getUpdateManager();
36300 _handleRefresh : function(url, params, loadOnce){
36301 if(!loadOnce || !this.loaded){
36302 var updater = this.bodyEl.getUpdateManager();
36303 updater.update(url, params, this._setLoaded.createDelegate(this));
36308 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
36309 * Will fail silently if the setUrl method has not been called.
36310 * This does not activate the panel, just updates its content.
36312 refresh : function(){
36313 if(this.refreshDelegate){
36314 this.loaded = false;
36315 this.refreshDelegate();
36320 _setLoaded : function(){
36321 this.loaded = true;
36325 closeClick : function(e){
36328 this.fireEvent("beforeclose", this, o);
36329 if(o.cancel !== true){
36330 this.tabPanel.removeTab(this.id);
36334 * The text displayed in the tooltip for the close icon.
36337 closeText : "Close this tab"