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 ( 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)
1527 this.el.select('img', true).first().dom.src = url;
1543 * @class Roo.bootstrap.Link
1544 * @extends Roo.bootstrap.Component
1545 * Bootstrap Link Class
1546 * @cfg {String} alt image alternative text
1547 * @cfg {String} href a tag href
1548 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1549 * @cfg {String} html the content of the link.
1550 * @cfg {String} anchor name for the anchor link
1551 * @cfg {String} fa - favicon
1553 * @cfg {Boolean} preventDefault (true | false) default false
1557 * Create a new Input
1558 * @param {Object} config The config object
1561 Roo.bootstrap.Link = function(config){
1562 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1568 * The img click event for the img.
1569 * @param {Roo.EventObject} e
1575 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1579 preventDefault: false,
1585 getAutoCreate : function()
1587 var html = this.html || '';
1589 if (this.fa !== false) {
1590 html = '<i class="fa fa-' + this.fa + '"></i>';
1595 // anchor's do not require html/href...
1596 if (this.anchor === false) {
1598 cfg.href = this.href || '#';
1600 cfg.name = this.anchor;
1601 if (this.html !== false || this.fa !== false) {
1604 if (this.href !== false) {
1605 cfg.href = this.href;
1609 if(this.alt !== false){
1614 if(this.target !== false) {
1615 cfg.target = this.target;
1621 initEvents: function() {
1623 if(!this.href || this.preventDefault){
1624 this.el.on('click', this.onClick, this);
1628 onClick : function(e)
1630 if(this.preventDefault){
1633 //Roo.log('img onclick');
1634 this.fireEvent('click', this, e);
1647 * @class Roo.bootstrap.Header
1648 * @extends Roo.bootstrap.Component
1649 * Bootstrap Header class
1650 * @cfg {String} html content of header
1651 * @cfg {Number} level (1|2|3|4|5|6) default 1
1654 * Create a new Header
1655 * @param {Object} config The config object
1659 Roo.bootstrap.Header = function(config){
1660 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1663 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1671 getAutoCreate : function(){
1676 tag: 'h' + (1 *this.level),
1677 html: this.html || ''
1689 * Ext JS Library 1.1.1
1690 * Copyright(c) 2006-2007, Ext JS, LLC.
1692 * Originally Released Under LGPL - original licence link has changed is not relivant.
1695 * <script type="text/javascript">
1699 * @class Roo.bootstrap.MenuMgr
1700 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1703 Roo.bootstrap.MenuMgr = function(){
1704 var menus, active, groups = {}, attached = false, lastShow = new Date();
1706 // private - called when first menu is created
1709 active = new Roo.util.MixedCollection();
1710 Roo.get(document).addKeyListener(27, function(){
1711 if(active.length > 0){
1719 if(active && active.length > 0){
1720 var c = active.clone();
1730 if(active.length < 1){
1731 Roo.get(document).un("mouseup", onMouseDown);
1739 var last = active.last();
1740 lastShow = new Date();
1743 Roo.get(document).on("mouseup", onMouseDown);
1748 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1749 m.parentMenu.activeChild = m;
1750 }else if(last && last.isVisible()){
1751 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1756 function onBeforeHide(m){
1758 m.activeChild.hide();
1760 if(m.autoHideTimer){
1761 clearTimeout(m.autoHideTimer);
1762 delete m.autoHideTimer;
1767 function onBeforeShow(m){
1768 var pm = m.parentMenu;
1769 if(!pm && !m.allowOtherMenus){
1771 }else if(pm && pm.activeChild && active != m){
1772 pm.activeChild.hide();
1776 // private this should really trigger on mouseup..
1777 function onMouseDown(e){
1778 Roo.log("on Mouse Up");
1780 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1781 Roo.log("MenuManager hideAll");
1790 function onBeforeCheck(mi, state){
1792 var g = groups[mi.group];
1793 for(var i = 0, l = g.length; i < l; i++){
1795 g[i].setChecked(false);
1804 * Hides all menus that are currently visible
1806 hideAll : function(){
1811 register : function(menu){
1815 menus[menu.id] = menu;
1816 menu.on("beforehide", onBeforeHide);
1817 menu.on("hide", onHide);
1818 menu.on("beforeshow", onBeforeShow);
1819 menu.on("show", onShow);
1821 if(g && menu.events["checkchange"]){
1825 groups[g].push(menu);
1826 menu.on("checkchange", onCheck);
1831 * Returns a {@link Roo.menu.Menu} object
1832 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1833 * be used to generate and return a new Menu instance.
1835 get : function(menu){
1836 if(typeof menu == "string"){ // menu id
1838 }else if(menu.events){ // menu instance
1841 /*else if(typeof menu.length == 'number'){ // array of menu items?
1842 return new Roo.bootstrap.Menu({items:menu});
1843 }else{ // otherwise, must be a config
1844 return new Roo.bootstrap.Menu(menu);
1851 unregister : function(menu){
1852 delete menus[menu.id];
1853 menu.un("beforehide", onBeforeHide);
1854 menu.un("hide", onHide);
1855 menu.un("beforeshow", onBeforeShow);
1856 menu.un("show", onShow);
1858 if(g && menu.events["checkchange"]){
1859 groups[g].remove(menu);
1860 menu.un("checkchange", onCheck);
1865 registerCheckable : function(menuItem){
1866 var g = menuItem.group;
1871 groups[g].push(menuItem);
1872 menuItem.on("beforecheckchange", onBeforeCheck);
1877 unregisterCheckable : function(menuItem){
1878 var g = menuItem.group;
1880 groups[g].remove(menuItem);
1881 menuItem.un("beforecheckchange", onBeforeCheck);
1893 * @class Roo.bootstrap.Menu
1894 * @extends Roo.bootstrap.Component
1895 * Bootstrap Menu class - container for MenuItems
1896 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1897 * @cfg {bool} hidden if the menu should be hidden when rendered.
1898 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1899 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1903 * @param {Object} config The config object
1907 Roo.bootstrap.Menu = function(config){
1908 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1909 if (this.registerMenu && this.type != 'treeview') {
1910 Roo.bootstrap.MenuMgr.register(this);
1915 * Fires before this menu is displayed
1916 * @param {Roo.menu.Menu} this
1921 * Fires before this menu is hidden
1922 * @param {Roo.menu.Menu} this
1927 * Fires after this menu is displayed
1928 * @param {Roo.menu.Menu} this
1933 * Fires after this menu is hidden
1934 * @param {Roo.menu.Menu} this
1939 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1940 * @param {Roo.menu.Menu} this
1941 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1942 * @param {Roo.EventObject} e
1947 * Fires when the mouse is hovering over this menu
1948 * @param {Roo.menu.Menu} this
1949 * @param {Roo.EventObject} e
1950 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1955 * Fires when the mouse exits this menu
1956 * @param {Roo.menu.Menu} this
1957 * @param {Roo.EventObject} e
1958 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1963 * Fires when a menu item contained in this menu is clicked
1964 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1965 * @param {Roo.EventObject} e
1969 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1972 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1976 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1979 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1981 registerMenu : true,
1983 menuItems :false, // stores the menu items..
1993 getChildContainer : function() {
1997 getAutoCreate : function(){
1999 //if (['right'].indexOf(this.align)!==-1) {
2000 // cfg.cn[1].cls += ' pull-right'
2006 cls : 'dropdown-menu' ,
2007 style : 'z-index:1000'
2011 if (this.type === 'submenu') {
2012 cfg.cls = 'submenu active';
2014 if (this.type === 'treeview') {
2015 cfg.cls = 'treeview-menu';
2020 initEvents : function() {
2022 // Roo.log("ADD event");
2023 // Roo.log(this.triggerEl.dom);
2025 this.triggerEl.on('click', this.onTriggerClick, this);
2027 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2029 this.triggerEl.addClass('dropdown-toggle');
2032 this.el.on('touchstart' , this.onTouch, this);
2034 this.el.on('click' , this.onClick, this);
2036 this.el.on("mouseover", this.onMouseOver, this);
2037 this.el.on("mouseout", this.onMouseOut, this);
2041 findTargetItem : function(e)
2043 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2047 //Roo.log(t); Roo.log(t.id);
2049 //Roo.log(this.menuitems);
2050 return this.menuitems.get(t.id);
2052 //return this.items.get(t.menuItemId);
2058 onTouch : function(e)
2060 Roo.log("menu.onTouch");
2061 //e.stopEvent(); this make the user popdown broken
2065 onClick : function(e)
2067 Roo.log("menu.onClick");
2069 var t = this.findTargetItem(e);
2070 if(!t || t.isContainer){
2075 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2076 if(t == this.activeItem && t.shouldDeactivate(e)){
2077 this.activeItem.deactivate();
2078 delete this.activeItem;
2082 this.setActiveItem(t, true);
2090 Roo.log('pass click event');
2094 this.fireEvent("click", this, t, e);
2098 (function() { _this.hide(); }).defer(100);
2101 onMouseOver : function(e){
2102 var t = this.findTargetItem(e);
2105 // if(t.canActivate && !t.disabled){
2106 // this.setActiveItem(t, true);
2110 this.fireEvent("mouseover", this, e, t);
2112 isVisible : function(){
2113 return !this.hidden;
2115 onMouseOut : function(e){
2116 var t = this.findTargetItem(e);
2119 // if(t == this.activeItem && t.shouldDeactivate(e)){
2120 // this.activeItem.deactivate();
2121 // delete this.activeItem;
2124 this.fireEvent("mouseout", this, e, t);
2129 * Displays this menu relative to another element
2130 * @param {String/HTMLElement/Roo.Element} element The element to align to
2131 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2132 * the element (defaults to this.defaultAlign)
2133 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2135 show : function(el, pos, parentMenu){
2136 this.parentMenu = parentMenu;
2140 this.fireEvent("beforeshow", this);
2141 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2144 * Displays this menu at a specific xy position
2145 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2146 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2148 showAt : function(xy, parentMenu, /* private: */_e){
2149 this.parentMenu = parentMenu;
2154 this.fireEvent("beforeshow", this);
2155 //xy = this.el.adjustForConstraints(xy);
2159 this.hideMenuItems();
2160 this.hidden = false;
2161 this.triggerEl.addClass('open');
2163 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2164 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2167 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2172 this.fireEvent("show", this);
2178 this.doFocus.defer(50, this);
2182 doFocus : function(){
2184 this.focusEl.focus();
2189 * Hides this menu and optionally all parent menus
2190 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2192 hide : function(deep)
2195 this.hideMenuItems();
2196 if(this.el && this.isVisible()){
2197 this.fireEvent("beforehide", this);
2198 if(this.activeItem){
2199 this.activeItem.deactivate();
2200 this.activeItem = null;
2202 this.triggerEl.removeClass('open');;
2204 this.fireEvent("hide", this);
2206 if(deep === true && this.parentMenu){
2207 this.parentMenu.hide(true);
2211 onTriggerClick : function(e)
2213 Roo.log('trigger click');
2215 var target = e.getTarget();
2217 Roo.log(target.nodeName.toLowerCase());
2219 if(target.nodeName.toLowerCase() === 'i'){
2225 onTriggerPress : function(e)
2227 Roo.log('trigger press');
2228 //Roo.log(e.getTarget());
2229 // Roo.log(this.triggerEl.dom);
2231 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2232 var pel = Roo.get(e.getTarget());
2233 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2234 Roo.log('is treeview or dropdown?');
2238 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2242 if (this.isVisible()) {
2247 this.show(this.triggerEl, false, false);
2250 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2257 hideMenuItems : function()
2259 Roo.log("hide Menu Items");
2263 //$(backdrop).remove()
2264 this.el.select('.open',true).each(function(aa) {
2266 aa.removeClass('open');
2267 //var parent = getParent($(this))
2268 //var relatedTarget = { relatedTarget: this }
2270 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2271 //if (e.isDefaultPrevented()) return
2272 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2275 addxtypeChild : function (tree, cntr) {
2276 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2278 this.menuitems.add(comp);
2299 * @class Roo.bootstrap.MenuItem
2300 * @extends Roo.bootstrap.Component
2301 * Bootstrap MenuItem class
2302 * @cfg {String} html the menu label
2303 * @cfg {String} href the link
2304 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2305 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2306 * @cfg {Boolean} active used on sidebars to highlight active itesm
2307 * @cfg {String} fa favicon to show on left of menu item.
2308 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2312 * Create a new MenuItem
2313 * @param {Object} config The config object
2317 Roo.bootstrap.MenuItem = function(config){
2318 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2323 * The raw click event for the entire grid.
2324 * @param {Roo.bootstrap.MenuItem} this
2325 * @param {Roo.EventObject} e
2331 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2335 preventDefault: false,
2336 isContainer : false,
2340 getAutoCreate : function(){
2342 if(this.isContainer){
2345 cls: 'dropdown-menu-item'
2359 if (this.fa !== false) {
2362 cls : 'fa fa-' + this.fa
2371 cls: 'dropdown-menu-item',
2374 if (this.parent().type == 'treeview') {
2375 cfg.cls = 'treeview-menu';
2378 cfg.cls += ' active';
2383 anc.href = this.href || cfg.cn[0].href ;
2384 ctag.html = this.html || cfg.cn[0].html ;
2388 initEvents: function()
2390 if (this.parent().type == 'treeview') {
2391 this.el.select('a').on('click', this.onClick, this);
2394 this.menu.parentType = this.xtype;
2395 this.menu.triggerEl = this.el;
2396 this.menu = this.addxtype(Roo.apply({}, this.menu));
2400 onClick : function(e)
2402 Roo.log('item on click ');
2404 if(this.preventDefault){
2407 //this.parent().hideMenuItems();
2409 this.fireEvent('click', this, e);
2428 * @class Roo.bootstrap.MenuSeparator
2429 * @extends Roo.bootstrap.Component
2430 * Bootstrap MenuSeparator class
2433 * Create a new MenuItem
2434 * @param {Object} config The config object
2438 Roo.bootstrap.MenuSeparator = function(config){
2439 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2442 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2444 getAutoCreate : function(){
2463 * @class Roo.bootstrap.Modal
2464 * @extends Roo.bootstrap.Component
2465 * Bootstrap Modal class
2466 * @cfg {String} title Title of dialog
2467 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2468 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2469 * @cfg {Boolean} specificTitle default false
2470 * @cfg {Array} buttons Array of buttons or standard button set..
2471 * @cfg {String} buttonPosition (left|right|center) default right
2472 * @cfg {Boolean} animate default true
2473 * @cfg {Boolean} allow_close default true
2474 * @cfg {Boolean} fitwindow default false
2475 * @cfg {String} size (sm|lg) default empty
2479 * Create a new Modal Dialog
2480 * @param {Object} config The config object
2483 Roo.bootstrap.Modal = function(config){
2484 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2489 * The raw btnclick event for the button
2490 * @param {Roo.EventObject} e
2494 this.buttons = this.buttons || [];
2497 this.tmpl = Roo.factory(this.tmpl);
2502 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2504 title : 'test dialog',
2514 specificTitle: false,
2516 buttonPosition: 'right',
2535 onRender : function(ct, position)
2537 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2540 var cfg = Roo.apply({}, this.getAutoCreate());
2543 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2545 //if (!cfg.name.length) {
2549 cfg.cls += ' ' + this.cls;
2552 cfg.style = this.style;
2554 this.el = Roo.get(document.body).createChild(cfg, position);
2556 //var type = this.el.dom.type;
2559 if(this.tabIndex !== undefined){
2560 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2563 this.dialogEl = this.el.select('.modal-dialog',true).first();
2564 this.bodyEl = this.el.select('.modal-body',true).first();
2565 this.closeEl = this.el.select('.modal-header .close', true).first();
2566 this.footerEl = this.el.select('.modal-footer',true).first();
2567 this.titleEl = this.el.select('.modal-title',true).first();
2571 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2572 this.maskEl.enableDisplayMode("block");
2574 //this.el.addClass("x-dlg-modal");
2576 if (this.buttons.length) {
2577 Roo.each(this.buttons, function(bb) {
2578 var b = Roo.apply({}, bb);
2579 b.xns = b.xns || Roo.bootstrap;
2580 b.xtype = b.xtype || 'Button';
2581 if (typeof(b.listeners) == 'undefined') {
2582 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2585 var btn = Roo.factory(b);
2587 btn.render(this.el.select('.modal-footer div').first());
2591 // render the children.
2594 if(typeof(this.items) != 'undefined'){
2595 var items = this.items;
2598 for(var i =0;i < items.length;i++) {
2599 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2603 this.items = nitems;
2605 // where are these used - they used to be body/close/footer
2609 //this.el.addClass([this.fieldClass, this.cls]);
2613 getAutoCreate : function(){
2618 html : this.html || ''
2623 cls : 'modal-title',
2627 if(this.specificTitle){
2633 if (this.allow_close) {
2645 if(this.size.length){
2646 size = 'modal-' + this.size;
2651 style : 'display: none',
2654 cls: "modal-dialog " + size,
2657 cls : "modal-content",
2660 cls : 'modal-header',
2665 cls : 'modal-footer',
2669 cls: 'btn-' + this.buttonPosition
2686 modal.cls += ' fade';
2692 getChildContainer : function() {
2697 getButtonContainer : function() {
2698 return this.el.select('.modal-footer div',true).first();
2701 initEvents : function()
2703 if (this.allow_close) {
2704 this.closeEl.on('click', this.hide, this);
2706 Roo.EventManager.onWindowResize(this.resize, this, true);
2713 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2714 if (this.fitwindow) {
2715 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2716 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2721 setSize : function(w,h)
2731 if (!this.rendered) {
2735 this.el.setStyle('display', 'block');
2737 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2740 this.el.addClass('in');
2743 this.el.addClass('in');
2747 // not sure how we can show data in here..
2749 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2752 Roo.get(document.body).addClass("x-body-masked");
2753 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2755 this.el.setStyle('zIndex', '10001');
2757 this.fireEvent('show', this);
2762 this.items.forEach( function(e) {
2763 e.layout ? e.layout() : false;
2771 if(this.fireEvent("beforehide", this) !== false){
2773 Roo.get(document.body).removeClass("x-body-masked");
2774 this.el.removeClass('in');
2775 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2777 if(this.animate){ // why
2779 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2781 this.el.setStyle('display', 'none');
2783 this.fireEvent('hide', this);
2787 addButton : function(str, cb)
2791 var b = Roo.apply({}, { html : str } );
2792 b.xns = b.xns || Roo.bootstrap;
2793 b.xtype = b.xtype || 'Button';
2794 if (typeof(b.listeners) == 'undefined') {
2795 b.listeners = { click : cb.createDelegate(this) };
2798 var btn = Roo.factory(b);
2800 btn.render(this.el.select('.modal-footer div').first());
2806 setDefaultButton : function(btn)
2808 //this.el.select('.modal-footer').()
2812 resizeTo: function(w,h)
2816 this.dialogEl.setWidth(w);
2817 if (this.diff === false) {
2818 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2821 this.bodyEl.setHeight(h-this.diff);
2825 setContentSize : function(w, h)
2829 onButtonClick: function(btn,e)
2832 this.fireEvent('btnclick', btn.name, e);
2835 * Set the title of the Dialog
2836 * @param {String} str new Title
2838 setTitle: function(str) {
2839 this.titleEl.dom.innerHTML = str;
2842 * Set the body of the Dialog
2843 * @param {String} str new Title
2845 setBody: function(str) {
2846 this.bodyEl.dom.innerHTML = str;
2849 * Set the body of the Dialog using the template
2850 * @param {Obj} data - apply this data to the template and replace the body contents.
2852 applyBody: function(obj)
2855 Roo.log("Error - using apply Body without a template");
2858 this.tmpl.overwrite(this.bodyEl, obj);
2864 Roo.apply(Roo.bootstrap.Modal, {
2866 * Button config that displays a single OK button
2875 * Button config that displays Yes and No buttons
2891 * Button config that displays OK and Cancel buttons
2906 * Button config that displays Yes, No and Cancel buttons
2928 * messagebox - can be used as a replace
2932 * @class Roo.MessageBox
2933 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2937 Roo.Msg.alert('Status', 'Changes saved successfully.');
2939 // Prompt for user data:
2940 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2942 // process text value...
2946 // Show a dialog using config options:
2948 title:'Save Changes?',
2949 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2950 buttons: Roo.Msg.YESNOCANCEL,
2957 Roo.bootstrap.MessageBox = function(){
2958 var dlg, opt, mask, waitTimer;
2959 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2960 var buttons, activeTextEl, bwidth;
2964 var handleButton = function(button){
2966 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2970 var handleHide = function(){
2972 dlg.el.removeClass(opt.cls);
2975 // Roo.TaskMgr.stop(waitTimer);
2976 // waitTimer = null;
2981 var updateButtons = function(b){
2984 buttons["ok"].hide();
2985 buttons["cancel"].hide();
2986 buttons["yes"].hide();
2987 buttons["no"].hide();
2988 //dlg.footer.dom.style.display = 'none';
2991 dlg.footerEl.dom.style.display = '';
2992 for(var k in buttons){
2993 if(typeof buttons[k] != "function"){
2996 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2997 width += buttons[k].el.getWidth()+15;
3007 var handleEsc = function(d, k, e){
3008 if(opt && opt.closable !== false){
3018 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3019 * @return {Roo.BasicDialog} The BasicDialog element
3021 getDialog : function(){
3023 dlg = new Roo.bootstrap.Modal( {
3026 //constraintoviewport:false,
3028 //collapsible : false,
3033 //buttonAlign:"center",
3034 closeClick : function(){
3035 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3038 handleButton("cancel");
3043 dlg.on("hide", handleHide);
3045 //dlg.addKeyListener(27, handleEsc);
3047 this.buttons = buttons;
3048 var bt = this.buttonText;
3049 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3050 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3051 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3052 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3054 bodyEl = dlg.bodyEl.createChild({
3056 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3057 '<textarea class="roo-mb-textarea"></textarea>' +
3058 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3060 msgEl = bodyEl.dom.firstChild;
3061 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3062 textboxEl.enableDisplayMode();
3063 textboxEl.addKeyListener([10,13], function(){
3064 if(dlg.isVisible() && opt && opt.buttons){
3067 }else if(opt.buttons.yes){
3068 handleButton("yes");
3072 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3073 textareaEl.enableDisplayMode();
3074 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3075 progressEl.enableDisplayMode();
3076 var pf = progressEl.dom.firstChild;
3078 pp = Roo.get(pf.firstChild);
3079 pp.setHeight(pf.offsetHeight);
3087 * Updates the message box body text
3088 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3089 * the XHTML-compliant non-breaking space character '&#160;')
3090 * @return {Roo.MessageBox} This message box
3092 updateText : function(text){
3093 if(!dlg.isVisible() && !opt.width){
3094 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3096 msgEl.innerHTML = text || ' ';
3098 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3099 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3101 Math.min(opt.width || cw , this.maxWidth),
3102 Math.max(opt.minWidth || this.minWidth, bwidth)
3105 activeTextEl.setWidth(w);
3107 if(dlg.isVisible()){
3108 dlg.fixedcenter = false;
3110 // to big, make it scroll. = But as usual stupid IE does not support
3113 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3114 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3115 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3117 bodyEl.dom.style.height = '';
3118 bodyEl.dom.style.overflowY = '';
3121 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3123 bodyEl.dom.style.overflowX = '';
3126 dlg.setContentSize(w, bodyEl.getHeight());
3127 if(dlg.isVisible()){
3128 dlg.fixedcenter = true;
3134 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3135 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3136 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3137 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3138 * @return {Roo.MessageBox} This message box
3140 updateProgress : function(value, text){
3142 this.updateText(text);
3144 if (pp) { // weird bug on my firefox - for some reason this is not defined
3145 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3151 * Returns true if the message box is currently displayed
3152 * @return {Boolean} True if the message box is visible, else false
3154 isVisible : function(){
3155 return dlg && dlg.isVisible();
3159 * Hides the message box if it is displayed
3162 if(this.isVisible()){
3168 * Displays a new message box, or reinitializes an existing message box, based on the config options
3169 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3170 * The following config object properties are supported:
3172 Property Type Description
3173 ---------- --------------- ------------------------------------------------------------------------------------
3174 animEl String/Element An id or Element from which the message box should animate as it opens and
3175 closes (defaults to undefined)
3176 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3177 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3178 closable Boolean False to hide the top-right close button (defaults to true). Note that
3179 progress and wait dialogs will ignore this property and always hide the
3180 close button as they can only be closed programmatically.
3181 cls String A custom CSS class to apply to the message box element
3182 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3183 displayed (defaults to 75)
3184 fn Function A callback function to execute after closing the dialog. The arguments to the
3185 function will be btn (the name of the button that was clicked, if applicable,
3186 e.g. "ok"), and text (the value of the active text field, if applicable).
3187 Progress and wait dialogs will ignore this option since they do not respond to
3188 user actions and can only be closed programmatically, so any required function
3189 should be called by the same code after it closes the dialog.
3190 icon String A CSS class that provides a background image to be used as an icon for
3191 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3192 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3193 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3194 modal Boolean False to allow user interaction with the page while the message box is
3195 displayed (defaults to true)
3196 msg String A string that will replace the existing message box body text (defaults
3197 to the XHTML-compliant non-breaking space character ' ')
3198 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3199 progress Boolean True to display a progress bar (defaults to false)
3200 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3201 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3202 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3203 title String The title text
3204 value String The string value to set into the active textbox element if displayed
3205 wait Boolean True to display a progress bar (defaults to false)
3206 width Number The width of the dialog in pixels
3213 msg: 'Please enter your address:',
3215 buttons: Roo.MessageBox.OKCANCEL,
3218 animEl: 'addAddressBtn'
3221 * @param {Object} config Configuration options
3222 * @return {Roo.MessageBox} This message box
3224 show : function(options)
3227 // this causes nightmares if you show one dialog after another
3228 // especially on callbacks..
3230 if(this.isVisible()){
3233 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3234 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3235 Roo.log("New Dialog Message:" + options.msg )
3236 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3237 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3240 var d = this.getDialog();
3242 d.setTitle(opt.title || " ");
3243 d.closeEl.setDisplayed(opt.closable !== false);
3244 activeTextEl = textboxEl;
3245 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3250 textareaEl.setHeight(typeof opt.multiline == "number" ?
3251 opt.multiline : this.defaultTextHeight);
3252 activeTextEl = textareaEl;
3261 progressEl.setDisplayed(opt.progress === true);
3262 this.updateProgress(0);
3263 activeTextEl.dom.value = opt.value || "";
3265 dlg.setDefaultButton(activeTextEl);
3267 var bs = opt.buttons;
3271 }else if(bs && bs.yes){
3272 db = buttons["yes"];
3274 dlg.setDefaultButton(db);
3276 bwidth = updateButtons(opt.buttons);
3277 this.updateText(opt.msg);
3279 d.el.addClass(opt.cls);
3281 d.proxyDrag = opt.proxyDrag === true;
3282 d.modal = opt.modal !== false;
3283 d.mask = opt.modal !== false ? mask : false;
3285 // force it to the end of the z-index stack so it gets a cursor in FF
3286 document.body.appendChild(dlg.el.dom);
3287 d.animateTarget = null;
3288 d.show(options.animEl);
3294 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3295 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3296 * and closing the message box when the process is complete.
3297 * @param {String} title The title bar text
3298 * @param {String} msg The message box body text
3299 * @return {Roo.MessageBox} This message box
3301 progress : function(title, msg){
3308 minWidth: this.minProgressWidth,
3315 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3316 * If a callback function is passed it will be called after the user clicks the button, and the
3317 * id of the button that was clicked will be passed as the only parameter to the callback
3318 * (could also be the top-right close button).
3319 * @param {String} title The title bar text
3320 * @param {String} msg The message box body text
3321 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3322 * @param {Object} scope (optional) The scope of the callback function
3323 * @return {Roo.MessageBox} This message box
3325 alert : function(title, msg, fn, scope){
3338 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3339 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3340 * You are responsible for closing the message box when the process is complete.
3341 * @param {String} msg The message box body text
3342 * @param {String} title (optional) The title bar text
3343 * @return {Roo.MessageBox} This message box
3345 wait : function(msg, title){
3356 waitTimer = Roo.TaskMgr.start({
3358 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3366 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3367 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3368 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3369 * @param {String} title The title bar text
3370 * @param {String} msg The message box body text
3371 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3372 * @param {Object} scope (optional) The scope of the callback function
3373 * @return {Roo.MessageBox} This message box
3375 confirm : function(title, msg, fn, scope){
3379 buttons: this.YESNO,
3388 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3389 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3390 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3391 * (could also be the top-right close button) and the text that was entered will be passed as the two
3392 * parameters to the callback.
3393 * @param {String} title The title bar text
3394 * @param {String} msg The message box body text
3395 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3396 * @param {Object} scope (optional) The scope of the callback function
3397 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3398 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3399 * @return {Roo.MessageBox} This message box
3401 prompt : function(title, msg, fn, scope, multiline){
3405 buttons: this.OKCANCEL,
3410 multiline: multiline,
3417 * Button config that displays a single OK button
3422 * Button config that displays Yes and No buttons
3425 YESNO : {yes:true, no:true},
3427 * Button config that displays OK and Cancel buttons
3430 OKCANCEL : {ok:true, cancel:true},
3432 * Button config that displays Yes, No and Cancel buttons
3435 YESNOCANCEL : {yes:true, no:true, cancel:true},
3438 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3441 defaultTextHeight : 75,
3443 * The maximum width in pixels of the message box (defaults to 600)
3448 * The minimum width in pixels of the message box (defaults to 100)
3453 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3454 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3457 minProgressWidth : 250,
3459 * An object containing the default button text strings that can be overriden for localized language support.
3460 * Supported properties are: ok, cancel, yes and no.
3461 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3474 * Shorthand for {@link Roo.MessageBox}
3476 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3477 Roo.Msg = Roo.Msg || Roo.MessageBox;
3486 * @class Roo.bootstrap.Navbar
3487 * @extends Roo.bootstrap.Component
3488 * Bootstrap Navbar class
3491 * Create a new Navbar
3492 * @param {Object} config The config object
3496 Roo.bootstrap.Navbar = function(config){
3497 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3501 * @event beforetoggle
3502 * Fire before toggle the menu
3503 * @param {Roo.EventObject} e
3505 "beforetoggle" : true
3509 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3518 getAutoCreate : function(){
3521 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3525 initEvents :function ()
3527 //Roo.log(this.el.select('.navbar-toggle',true));
3528 this.el.select('.navbar-toggle',true).on('click', function() {
3529 if(this.fireEvent('beforetoggle', this) !== false){
3530 this.el.select('.navbar-collapse',true).toggleClass('in');
3540 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3542 var size = this.el.getSize();
3543 this.maskEl.setSize(size.width, size.height);
3544 this.maskEl.enableDisplayMode("block");
3553 getChildContainer : function()
3555 if (this.el.select('.collapse').getCount()) {
3556 return this.el.select('.collapse',true).first();
3589 * @class Roo.bootstrap.NavSimplebar
3590 * @extends Roo.bootstrap.Navbar
3591 * Bootstrap Sidebar class
3593 * @cfg {Boolean} inverse is inverted color
3595 * @cfg {String} type (nav | pills | tabs)
3596 * @cfg {Boolean} arrangement stacked | justified
3597 * @cfg {String} align (left | right) alignment
3599 * @cfg {Boolean} main (true|false) main nav bar? default false
3600 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3602 * @cfg {String} tag (header|footer|nav|div) default is nav
3608 * Create a new Sidebar
3609 * @param {Object} config The config object
3613 Roo.bootstrap.NavSimplebar = function(config){
3614 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3617 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3633 getAutoCreate : function(){
3637 tag : this.tag || 'div',
3650 this.type = this.type || 'nav';
3651 if (['tabs','pills'].indexOf(this.type)!==-1) {
3652 cfg.cn[0].cls += ' nav-' + this.type
3656 if (this.type!=='nav') {
3657 Roo.log('nav type must be nav/tabs/pills')
3659 cfg.cn[0].cls += ' navbar-nav'
3665 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3666 cfg.cn[0].cls += ' nav-' + this.arrangement;
3670 if (this.align === 'right') {
3671 cfg.cn[0].cls += ' navbar-right';
3675 cfg.cls += ' navbar-inverse';
3702 * @class Roo.bootstrap.NavHeaderbar
3703 * @extends Roo.bootstrap.NavSimplebar
3704 * Bootstrap Sidebar class
3706 * @cfg {String} brand what is brand
3707 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3708 * @cfg {String} brand_href href of the brand
3709 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3710 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3711 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3712 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3715 * Create a new Sidebar
3716 * @param {Object} config The config object
3720 Roo.bootstrap.NavHeaderbar = function(config){
3721 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3725 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3732 desktopCenter : false,
3735 getAutoCreate : function(){
3738 tag: this.nav || 'nav',
3745 if (this.desktopCenter) {
3746 cn.push({cls : 'container', cn : []});
3753 cls: 'navbar-header',
3758 cls: 'navbar-toggle',
3759 'data-toggle': 'collapse',
3764 html: 'Toggle navigation'
3786 cls: 'collapse navbar-collapse',
3790 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3792 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3793 cfg.cls += ' navbar-' + this.position;
3795 // tag can override this..
3797 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3800 if (this.brand !== '') {
3803 href: this.brand_href ? this.brand_href : '#',
3804 cls: 'navbar-brand',
3812 cfg.cls += ' main-nav';
3820 getHeaderChildContainer : function()
3822 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3823 return this.el.select('.navbar-header',true).first();
3826 return this.getChildContainer();
3830 initEvents : function()
3832 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3834 if (this.autohide) {
3839 Roo.get(document).on('scroll',function(e) {
3840 var ns = Roo.get(document).getScroll().top;
3841 var os = prevScroll;
3845 ft.removeClass('slideDown');
3846 ft.addClass('slideUp');
3849 ft.removeClass('slideUp');
3850 ft.addClass('slideDown');
3871 * @class Roo.bootstrap.NavSidebar
3872 * @extends Roo.bootstrap.Navbar
3873 * Bootstrap Sidebar class
3876 * Create a new Sidebar
3877 * @param {Object} config The config object
3881 Roo.bootstrap.NavSidebar = function(config){
3882 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3885 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3887 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3889 getAutoCreate : function(){
3894 cls: 'sidebar sidebar-nav'
3916 * @class Roo.bootstrap.NavGroup
3917 * @extends Roo.bootstrap.Component
3918 * Bootstrap NavGroup class
3919 * @cfg {String} align (left|right)
3920 * @cfg {Boolean} inverse
3921 * @cfg {String} type (nav|pills|tab) default nav
3922 * @cfg {String} navId - reference Id for navbar.
3926 * Create a new nav group
3927 * @param {Object} config The config object
3930 Roo.bootstrap.NavGroup = function(config){
3931 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3934 Roo.bootstrap.NavGroup.register(this);
3938 * Fires when the active item changes
3939 * @param {Roo.bootstrap.NavGroup} this
3940 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3941 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3948 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3959 getAutoCreate : function()
3961 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3968 if (['tabs','pills'].indexOf(this.type)!==-1) {
3969 cfg.cls += ' nav-' + this.type
3971 if (this.type!=='nav') {
3972 Roo.log('nav type must be nav/tabs/pills')
3974 cfg.cls += ' navbar-nav'
3977 if (this.parent().sidebar) {
3980 cls: 'dashboard-menu sidebar-menu'
3986 if (this.form === true) {
3992 if (this.align === 'right') {
3993 cfg.cls += ' navbar-right';
3995 cfg.cls += ' navbar-left';
3999 if (this.align === 'right') {
4000 cfg.cls += ' navbar-right';
4004 cfg.cls += ' navbar-inverse';
4012 * sets the active Navigation item
4013 * @param {Roo.bootstrap.NavItem} the new current navitem
4015 setActiveItem : function(item)
4018 Roo.each(this.navItems, function(v){
4023 v.setActive(false, true);
4030 item.setActive(true, true);
4031 this.fireEvent('changed', this, item, prev);
4036 * gets the active Navigation item
4037 * @return {Roo.bootstrap.NavItem} the current navitem
4039 getActive : function()
4043 Roo.each(this.navItems, function(v){
4054 indexOfNav : function()
4058 Roo.each(this.navItems, function(v,i){
4069 * adds a Navigation item
4070 * @param {Roo.bootstrap.NavItem} the navitem to add
4072 addItem : function(cfg)
4074 var cn = new Roo.bootstrap.NavItem(cfg);
4076 cn.parentId = this.id;
4077 cn.onRender(this.el, null);
4081 * register a Navigation item
4082 * @param {Roo.bootstrap.NavItem} the navitem to add
4084 register : function(item)
4086 this.navItems.push( item);
4087 item.navId = this.navId;
4092 * clear all the Navigation item
4095 clearAll : function()
4098 this.el.dom.innerHTML = '';
4101 getNavItem: function(tabId)
4104 Roo.each(this.navItems, function(e) {
4105 if (e.tabId == tabId) {
4115 setActiveNext : function()
4117 var i = this.indexOfNav(this.getActive());
4118 if (i > this.navItems.length) {
4121 this.setActiveItem(this.navItems[i+1]);
4123 setActivePrev : function()
4125 var i = this.indexOfNav(this.getActive());
4129 this.setActiveItem(this.navItems[i-1]);
4131 clearWasActive : function(except) {
4132 Roo.each(this.navItems, function(e) {
4133 if (e.tabId != except.tabId && e.was_active) {
4134 e.was_active = false;
4141 getWasActive : function ()
4144 Roo.each(this.navItems, function(e) {
4159 Roo.apply(Roo.bootstrap.NavGroup, {
4163 * register a Navigation Group
4164 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4166 register : function(navgrp)
4168 this.groups[navgrp.navId] = navgrp;
4172 * fetch a Navigation Group based on the navigation ID
4173 * @param {string} the navgroup to add
4174 * @returns {Roo.bootstrap.NavGroup} the navgroup
4176 get: function(navId) {
4177 if (typeof(this.groups[navId]) == 'undefined') {
4179 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4181 return this.groups[navId] ;
4196 * @class Roo.bootstrap.NavItem
4197 * @extends Roo.bootstrap.Component
4198 * Bootstrap Navbar.NavItem class
4199 * @cfg {String} href link to
4200 * @cfg {String} html content of button
4201 * @cfg {String} badge text inside badge
4202 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4203 * @cfg {String} glyphicon name of glyphicon
4204 * @cfg {String} icon name of font awesome icon
4205 * @cfg {Boolean} active Is item active
4206 * @cfg {Boolean} disabled Is item disabled
4208 * @cfg {Boolean} preventDefault (true | false) default false
4209 * @cfg {String} tabId the tab that this item activates.
4210 * @cfg {String} tagtype (a|span) render as a href or span?
4211 * @cfg {Boolean} animateRef (true|false) link to element default false
4214 * Create a new Navbar Item
4215 * @param {Object} config The config object
4217 Roo.bootstrap.NavItem = function(config){
4218 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4223 * The raw click event for the entire grid.
4224 * @param {Roo.EventObject} e
4229 * Fires when the active item active state changes
4230 * @param {Roo.bootstrap.NavItem} this
4231 * @param {boolean} state the new state
4237 * Fires when scroll to element
4238 * @param {Roo.bootstrap.NavItem} this
4239 * @param {Object} options
4240 * @param {Roo.EventObject} e
4248 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4256 preventDefault : false,
4263 getAutoCreate : function(){
4272 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4274 if (this.disabled) {
4275 cfg.cls += ' disabled';
4278 if (this.href || this.html || this.glyphicon || this.icon) {
4282 href : this.href || "#",
4283 html: this.html || ''
4288 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4291 if(this.glyphicon) {
4292 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4297 cfg.cn[0].html += " <span class='caret'></span>";
4301 if (this.badge !== '') {
4303 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4311 initEvents: function()
4313 if (typeof (this.menu) != 'undefined') {
4314 this.menu.parentType = this.xtype;
4315 this.menu.triggerEl = this.el;
4316 this.menu = this.addxtype(Roo.apply({}, this.menu));
4319 this.el.select('a',true).on('click', this.onClick, this);
4321 if(this.tagtype == 'span'){
4322 this.el.select('span',true).on('click', this.onClick, this);
4325 // at this point parent should be available..
4326 this.parent().register(this);
4329 onClick : function(e)
4331 if (e.getTarget('.dropdown-menu-item')) {
4332 // did you click on a menu itemm.... - then don't trigger onclick..
4337 this.preventDefault ||
4340 Roo.log("NavItem - prevent Default?");
4344 if (this.disabled) {
4348 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4349 if (tg && tg.transition) {
4350 Roo.log("waiting for the transitionend");
4356 //Roo.log("fire event clicked");
4357 if(this.fireEvent('click', this, e) === false){
4361 if(this.tagtype == 'span'){
4365 //Roo.log(this.href);
4366 var ael = this.el.select('a',true).first();
4369 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4370 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4371 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4372 return; // ignore... - it's a 'hash' to another page.
4374 Roo.log("NavItem - prevent Default?");
4376 this.scrollToElement(e);
4380 var p = this.parent();
4382 if (['tabs','pills'].indexOf(p.type)!==-1) {
4383 if (typeof(p.setActiveItem) !== 'undefined') {
4384 p.setActiveItem(this);
4388 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4389 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4390 // remove the collapsed menu expand...
4391 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4395 isActive: function () {
4398 setActive : function(state, fire, is_was_active)
4400 if (this.active && !state && this.navId) {
4401 this.was_active = true;
4402 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4404 nv.clearWasActive(this);
4408 this.active = state;
4411 this.el.removeClass('active');
4412 } else if (!this.el.hasClass('active')) {
4413 this.el.addClass('active');
4416 this.fireEvent('changed', this, state);
4419 // show a panel if it's registered and related..
4421 if (!this.navId || !this.tabId || !state || is_was_active) {
4425 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4429 var pan = tg.getPanelByName(this.tabId);
4433 // if we can not flip to new panel - go back to old nav highlight..
4434 if (false == tg.showPanel(pan)) {
4435 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4437 var onav = nv.getWasActive();
4439 onav.setActive(true, false, true);
4448 // this should not be here...
4449 setDisabled : function(state)
4451 this.disabled = state;
4453 this.el.removeClass('disabled');
4454 } else if (!this.el.hasClass('disabled')) {
4455 this.el.addClass('disabled');
4461 * Fetch the element to display the tooltip on.
4462 * @return {Roo.Element} defaults to this.el
4464 tooltipEl : function()
4466 return this.el.select('' + this.tagtype + '', true).first();
4469 scrollToElement : function(e)
4471 var c = document.body;
4474 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4476 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4477 c = document.documentElement;
4480 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4486 var o = target.calcOffsetsTo(c);
4493 this.fireEvent('scrollto', this, options, e);
4495 Roo.get(c).scrollTo('top', options.value, true);
4508 * <span> icon </span>
4509 * <span> text </span>
4510 * <span>badge </span>
4514 * @class Roo.bootstrap.NavSidebarItem
4515 * @extends Roo.bootstrap.NavItem
4516 * Bootstrap Navbar.NavSidebarItem class
4517 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4518 * {bool} open is the menu open
4520 * Create a new Navbar Button
4521 * @param {Object} config The config object
4523 Roo.bootstrap.NavSidebarItem = function(config){
4524 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4529 * The raw click event for the entire grid.
4530 * @param {Roo.EventObject} e
4535 * Fires when the active item active state changes
4536 * @param {Roo.bootstrap.NavSidebarItem} this
4537 * @param {boolean} state the new state
4545 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4547 badgeWeight : 'default',
4551 getAutoCreate : function(){
4556 href : this.href || '#',
4568 html : this.html || ''
4573 cfg.cls += ' active';
4576 if (this.disabled) {
4577 cfg.cls += ' disabled';
4580 cfg.cls += ' open x-open';
4583 if (this.glyphicon || this.icon) {
4584 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4585 a.cn.push({ tag : 'i', cls : c }) ;
4590 if (this.badge !== '') {
4592 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4596 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4597 a.cls += 'dropdown-toggle treeview' ;
4605 initEvents : function()
4607 if (typeof (this.menu) != 'undefined') {
4608 this.menu.parentType = this.xtype;
4609 this.menu.triggerEl = this.el;
4610 this.menu = this.addxtype(Roo.apply({}, this.menu));
4613 this.el.on('click', this.onClick, this);
4616 if(this.badge !== ''){
4618 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4623 onClick : function(e)
4630 if(this.preventDefault){
4634 this.fireEvent('click', this);
4637 disable : function()
4639 this.setDisabled(true);
4644 this.setDisabled(false);
4647 setDisabled : function(state)
4649 if(this.disabled == state){
4653 this.disabled = state;
4656 this.el.addClass('disabled');
4660 this.el.removeClass('disabled');
4665 setActive : function(state)
4667 if(this.active == state){
4671 this.active = state;
4674 this.el.addClass('active');
4678 this.el.removeClass('active');
4683 isActive: function ()
4688 setBadge : function(str)
4694 this.badgeEl.dom.innerHTML = str;
4711 * @class Roo.bootstrap.Row
4712 * @extends Roo.bootstrap.Component
4713 * Bootstrap Row class (contains columns...)
4717 * @param {Object} config The config object
4720 Roo.bootstrap.Row = function(config){
4721 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4724 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4726 getAutoCreate : function(){
4745 * @class Roo.bootstrap.Element
4746 * @extends Roo.bootstrap.Component
4747 * Bootstrap Element class
4748 * @cfg {String} html contents of the element
4749 * @cfg {String} tag tag of the element
4750 * @cfg {String} cls class of the element
4751 * @cfg {Boolean} preventDefault (true|false) default false
4752 * @cfg {Boolean} clickable (true|false) default false
4755 * Create a new Element
4756 * @param {Object} config The config object
4759 Roo.bootstrap.Element = function(config){
4760 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4766 * When a element is chick
4767 * @param {Roo.bootstrap.Element} this
4768 * @param {Roo.EventObject} e
4774 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4779 preventDefault: false,
4782 getAutoCreate : function(){
4793 initEvents: function()
4795 Roo.bootstrap.Element.superclass.initEvents.call(this);
4798 this.el.on('click', this.onClick, this);
4803 onClick : function(e)
4805 if(this.preventDefault){
4809 this.fireEvent('click', this, e);
4812 getValue : function()
4814 return this.el.dom.innerHTML;
4817 setValue : function(value)
4819 this.el.dom.innerHTML = value;
4834 * @class Roo.bootstrap.Pagination
4835 * @extends Roo.bootstrap.Component
4836 * Bootstrap Pagination class
4837 * @cfg {String} size xs | sm | md | lg
4838 * @cfg {Boolean} inverse false | true
4841 * Create a new Pagination
4842 * @param {Object} config The config object
4845 Roo.bootstrap.Pagination = function(config){
4846 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4849 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4855 getAutoCreate : function(){
4861 cfg.cls += ' inverse';
4867 cfg.cls += " " + this.cls;
4885 * @class Roo.bootstrap.PaginationItem
4886 * @extends Roo.bootstrap.Component
4887 * Bootstrap PaginationItem class
4888 * @cfg {String} html text
4889 * @cfg {String} href the link
4890 * @cfg {Boolean} preventDefault (true | false) default true
4891 * @cfg {Boolean} active (true | false) default false
4892 * @cfg {Boolean} disabled default false
4896 * Create a new PaginationItem
4897 * @param {Object} config The config object
4901 Roo.bootstrap.PaginationItem = function(config){
4902 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4907 * The raw click event for the entire grid.
4908 * @param {Roo.EventObject} e
4914 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4918 preventDefault: true,
4923 getAutoCreate : function(){
4929 href : this.href ? this.href : '#',
4930 html : this.html ? this.html : ''
4940 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4944 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4950 initEvents: function() {
4952 this.el.on('click', this.onClick, this);
4955 onClick : function(e)
4957 Roo.log('PaginationItem on click ');
4958 if(this.preventDefault){
4966 this.fireEvent('click', this, e);
4982 * @class Roo.bootstrap.Slider
4983 * @extends Roo.bootstrap.Component
4984 * Bootstrap Slider class
4987 * Create a new Slider
4988 * @param {Object} config The config object
4991 Roo.bootstrap.Slider = function(config){
4992 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4995 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4997 getAutoCreate : function(){
5001 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5005 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5017 * Ext JS Library 1.1.1
5018 * Copyright(c) 2006-2007, Ext JS, LLC.
5020 * Originally Released Under LGPL - original licence link has changed is not relivant.
5023 * <script type="text/javascript">
5028 * @class Roo.grid.ColumnModel
5029 * @extends Roo.util.Observable
5030 * This is the default implementation of a ColumnModel used by the Grid. It defines
5031 * the columns in the grid.
5034 var colModel = new Roo.grid.ColumnModel([
5035 {header: "Ticker", width: 60, sortable: true, locked: true},
5036 {header: "Company Name", width: 150, sortable: true},
5037 {header: "Market Cap.", width: 100, sortable: true},
5038 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5039 {header: "Employees", width: 100, sortable: true, resizable: false}
5044 * The config options listed for this class are options which may appear in each
5045 * individual column definition.
5046 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5048 * @param {Object} config An Array of column config objects. See this class's
5049 * config objects for details.
5051 Roo.grid.ColumnModel = function(config){
5053 * The config passed into the constructor
5055 this.config = config;
5058 // if no id, create one
5059 // if the column does not have a dataIndex mapping,
5060 // map it to the order it is in the config
5061 for(var i = 0, len = config.length; i < len; i++){
5063 if(typeof c.dataIndex == "undefined"){
5066 if(typeof c.renderer == "string"){
5067 c.renderer = Roo.util.Format[c.renderer];
5069 if(typeof c.id == "undefined"){
5072 if(c.editor && c.editor.xtype){
5073 c.editor = Roo.factory(c.editor, Roo.grid);
5075 if(c.editor && c.editor.isFormField){
5076 c.editor = new Roo.grid.GridEditor(c.editor);
5078 this.lookup[c.id] = c;
5082 * The width of columns which have no width specified (defaults to 100)
5085 this.defaultWidth = 100;
5088 * Default sortable of columns which have no sortable specified (defaults to false)
5091 this.defaultSortable = false;
5095 * @event widthchange
5096 * Fires when the width of a column changes.
5097 * @param {ColumnModel} this
5098 * @param {Number} columnIndex The column index
5099 * @param {Number} newWidth The new width
5101 "widthchange": true,
5103 * @event headerchange
5104 * Fires when the text of a header changes.
5105 * @param {ColumnModel} this
5106 * @param {Number} columnIndex The column index
5107 * @param {Number} newText The new header text
5109 "headerchange": true,
5111 * @event hiddenchange
5112 * Fires when a column is hidden or "unhidden".
5113 * @param {ColumnModel} this
5114 * @param {Number} columnIndex The column index
5115 * @param {Boolean} hidden true if hidden, false otherwise
5117 "hiddenchange": true,
5119 * @event columnmoved
5120 * Fires when a column is moved.
5121 * @param {ColumnModel} this
5122 * @param {Number} oldIndex
5123 * @param {Number} newIndex
5125 "columnmoved" : true,
5127 * @event columlockchange
5128 * Fires when a column's locked state is changed
5129 * @param {ColumnModel} this
5130 * @param {Number} colIndex
5131 * @param {Boolean} locked true if locked
5133 "columnlockchange" : true
5135 Roo.grid.ColumnModel.superclass.constructor.call(this);
5137 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5139 * @cfg {String} header The header text to display in the Grid view.
5142 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5143 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5144 * specified, the column's index is used as an index into the Record's data Array.
5147 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5148 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5151 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5152 * Defaults to the value of the {@link #defaultSortable} property.
5153 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5156 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5159 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5162 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5165 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5168 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5169 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5170 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5171 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5174 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5177 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5180 * @cfg {String} cursor (Optional)
5183 * @cfg {String} tooltip (Optional)
5186 * @cfg {Number} xs (Optional)
5189 * @cfg {Number} sm (Optional)
5192 * @cfg {Number} md (Optional)
5195 * @cfg {Number} lg (Optional)
5198 * Returns the id of the column at the specified index.
5199 * @param {Number} index The column index
5200 * @return {String} the id
5202 getColumnId : function(index){
5203 return this.config[index].id;
5207 * Returns the column for a specified id.
5208 * @param {String} id The column id
5209 * @return {Object} the column
5211 getColumnById : function(id){
5212 return this.lookup[id];
5217 * Returns the column for a specified dataIndex.
5218 * @param {String} dataIndex The column dataIndex
5219 * @return {Object|Boolean} the column or false if not found
5221 getColumnByDataIndex: function(dataIndex){
5222 var index = this.findColumnIndex(dataIndex);
5223 return index > -1 ? this.config[index] : false;
5227 * Returns the index for a specified column id.
5228 * @param {String} id The column id
5229 * @return {Number} the index, or -1 if not found
5231 getIndexById : function(id){
5232 for(var i = 0, len = this.config.length; i < len; i++){
5233 if(this.config[i].id == id){
5241 * Returns the index for a specified column dataIndex.
5242 * @param {String} dataIndex The column dataIndex
5243 * @return {Number} the index, or -1 if not found
5246 findColumnIndex : function(dataIndex){
5247 for(var i = 0, len = this.config.length; i < len; i++){
5248 if(this.config[i].dataIndex == dataIndex){
5256 moveColumn : function(oldIndex, newIndex){
5257 var c = this.config[oldIndex];
5258 this.config.splice(oldIndex, 1);
5259 this.config.splice(newIndex, 0, c);
5260 this.dataMap = null;
5261 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5264 isLocked : function(colIndex){
5265 return this.config[colIndex].locked === true;
5268 setLocked : function(colIndex, value, suppressEvent){
5269 if(this.isLocked(colIndex) == value){
5272 this.config[colIndex].locked = value;
5274 this.fireEvent("columnlockchange", this, colIndex, value);
5278 getTotalLockedWidth : function(){
5280 for(var i = 0; i < this.config.length; i++){
5281 if(this.isLocked(i) && !this.isHidden(i)){
5282 this.totalWidth += this.getColumnWidth(i);
5288 getLockedCount : function(){
5289 for(var i = 0, len = this.config.length; i < len; i++){
5290 if(!this.isLocked(i)){
5295 return this.config.length;
5299 * Returns the number of columns.
5302 getColumnCount : function(visibleOnly){
5303 if(visibleOnly === true){
5305 for(var i = 0, len = this.config.length; i < len; i++){
5306 if(!this.isHidden(i)){
5312 return this.config.length;
5316 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5317 * @param {Function} fn
5318 * @param {Object} scope (optional)
5319 * @return {Array} result
5321 getColumnsBy : function(fn, scope){
5323 for(var i = 0, len = this.config.length; i < len; i++){
5324 var c = this.config[i];
5325 if(fn.call(scope||this, c, i) === true){
5333 * Returns true if the specified column is sortable.
5334 * @param {Number} col The column index
5337 isSortable : function(col){
5338 if(typeof this.config[col].sortable == "undefined"){
5339 return this.defaultSortable;
5341 return this.config[col].sortable;
5345 * Returns the rendering (formatting) function defined for the column.
5346 * @param {Number} col The column index.
5347 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5349 getRenderer : function(col){
5350 if(!this.config[col].renderer){
5351 return Roo.grid.ColumnModel.defaultRenderer;
5353 return this.config[col].renderer;
5357 * Sets the rendering (formatting) function for a column.
5358 * @param {Number} col The column index
5359 * @param {Function} fn The function to use to process the cell's raw data
5360 * to return HTML markup for the grid view. The render function is called with
5361 * the following parameters:<ul>
5362 * <li>Data value.</li>
5363 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5364 * <li>css A CSS style string to apply to the table cell.</li>
5365 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5366 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5367 * <li>Row index</li>
5368 * <li>Column index</li>
5369 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5371 setRenderer : function(col, fn){
5372 this.config[col].renderer = fn;
5376 * Returns the width for the specified column.
5377 * @param {Number} col The column index
5380 getColumnWidth : function(col){
5381 return this.config[col].width * 1 || this.defaultWidth;
5385 * Sets the width for a column.
5386 * @param {Number} col The column index
5387 * @param {Number} width The new width
5389 setColumnWidth : function(col, width, suppressEvent){
5390 this.config[col].width = width;
5391 this.totalWidth = null;
5393 this.fireEvent("widthchange", this, col, width);
5398 * Returns the total width of all columns.
5399 * @param {Boolean} includeHidden True to include hidden column widths
5402 getTotalWidth : function(includeHidden){
5403 if(!this.totalWidth){
5404 this.totalWidth = 0;
5405 for(var i = 0, len = this.config.length; i < len; i++){
5406 if(includeHidden || !this.isHidden(i)){
5407 this.totalWidth += this.getColumnWidth(i);
5411 return this.totalWidth;
5415 * Returns the header for the specified column.
5416 * @param {Number} col The column index
5419 getColumnHeader : function(col){
5420 return this.config[col].header;
5424 * Sets the header for a column.
5425 * @param {Number} col The column index
5426 * @param {String} header The new header
5428 setColumnHeader : function(col, header){
5429 this.config[col].header = header;
5430 this.fireEvent("headerchange", this, col, header);
5434 * Returns the tooltip for the specified column.
5435 * @param {Number} col The column index
5438 getColumnTooltip : function(col){
5439 return this.config[col].tooltip;
5442 * Sets the tooltip for a column.
5443 * @param {Number} col The column index
5444 * @param {String} tooltip The new tooltip
5446 setColumnTooltip : function(col, tooltip){
5447 this.config[col].tooltip = tooltip;
5451 * Returns the dataIndex for the specified column.
5452 * @param {Number} col The column index
5455 getDataIndex : function(col){
5456 return this.config[col].dataIndex;
5460 * Sets the dataIndex for a column.
5461 * @param {Number} col The column index
5462 * @param {Number} dataIndex The new dataIndex
5464 setDataIndex : function(col, dataIndex){
5465 this.config[col].dataIndex = dataIndex;
5471 * Returns true if the cell is editable.
5472 * @param {Number} colIndex The column index
5473 * @param {Number} rowIndex The row index - this is nto actually used..?
5476 isCellEditable : function(colIndex, rowIndex){
5477 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5481 * Returns the editor defined for the cell/column.
5482 * return false or null to disable editing.
5483 * @param {Number} colIndex The column index
5484 * @param {Number} rowIndex The row index
5487 getCellEditor : function(colIndex, rowIndex){
5488 return this.config[colIndex].editor;
5492 * Sets if a column is editable.
5493 * @param {Number} col The column index
5494 * @param {Boolean} editable True if the column is editable
5496 setEditable : function(col, editable){
5497 this.config[col].editable = editable;
5502 * Returns true if the column is hidden.
5503 * @param {Number} colIndex The column index
5506 isHidden : function(colIndex){
5507 return this.config[colIndex].hidden;
5512 * Returns true if the column width cannot be changed
5514 isFixed : function(colIndex){
5515 return this.config[colIndex].fixed;
5519 * Returns true if the column can be resized
5522 isResizable : function(colIndex){
5523 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5526 * Sets if a column is hidden.
5527 * @param {Number} colIndex The column index
5528 * @param {Boolean} hidden True if the column is hidden
5530 setHidden : function(colIndex, hidden){
5531 this.config[colIndex].hidden = hidden;
5532 this.totalWidth = null;
5533 this.fireEvent("hiddenchange", this, colIndex, hidden);
5537 * Sets the editor for a column.
5538 * @param {Number} col The column index
5539 * @param {Object} editor The editor object
5541 setEditor : function(col, editor){
5542 this.config[col].editor = editor;
5546 Roo.grid.ColumnModel.defaultRenderer = function(value)
5548 if(typeof value == "object") {
5551 if(typeof value == "string" && value.length < 1){
5555 return String.format("{0}", value);
5558 // Alias for backwards compatibility
5559 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5562 * Ext JS Library 1.1.1
5563 * Copyright(c) 2006-2007, Ext JS, LLC.
5565 * Originally Released Under LGPL - original licence link has changed is not relivant.
5568 * <script type="text/javascript">
5572 * @class Roo.LoadMask
5573 * A simple utility class for generically masking elements while loading data. If the element being masked has
5574 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5575 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5576 * element's UpdateManager load indicator and will be destroyed after the initial load.
5578 * Create a new LoadMask
5579 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5580 * @param {Object} config The config object
5582 Roo.LoadMask = function(el, config){
5583 this.el = Roo.get(el);
5584 Roo.apply(this, config);
5586 this.store.on('beforeload', this.onBeforeLoad, this);
5587 this.store.on('load', this.onLoad, this);
5588 this.store.on('loadexception', this.onLoadException, this);
5589 this.removeMask = false;
5591 var um = this.el.getUpdateManager();
5592 um.showLoadIndicator = false; // disable the default indicator
5593 um.on('beforeupdate', this.onBeforeLoad, this);
5594 um.on('update', this.onLoad, this);
5595 um.on('failure', this.onLoad, this);
5596 this.removeMask = true;
5600 Roo.LoadMask.prototype = {
5602 * @cfg {Boolean} removeMask
5603 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5604 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5608 * The text to display in a centered loading message box (defaults to 'Loading...')
5612 * @cfg {String} msgCls
5613 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5615 msgCls : 'x-mask-loading',
5618 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5624 * Disables the mask to prevent it from being displayed
5626 disable : function(){
5627 this.disabled = true;
5631 * Enables the mask so that it can be displayed
5633 enable : function(){
5634 this.disabled = false;
5637 onLoadException : function()
5641 if (typeof(arguments[3]) != 'undefined') {
5642 Roo.MessageBox.alert("Error loading",arguments[3]);
5646 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5647 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5656 this.el.unmask(this.removeMask);
5661 this.el.unmask(this.removeMask);
5665 onBeforeLoad : function(){
5667 this.el.mask(this.msg, this.msgCls);
5672 destroy : function(){
5674 this.store.un('beforeload', this.onBeforeLoad, this);
5675 this.store.un('load', this.onLoad, this);
5676 this.store.un('loadexception', this.onLoadException, this);
5678 var um = this.el.getUpdateManager();
5679 um.un('beforeupdate', this.onBeforeLoad, this);
5680 um.un('update', this.onLoad, this);
5681 um.un('failure', this.onLoad, this);
5692 * @class Roo.bootstrap.Table
5693 * @extends Roo.bootstrap.Component
5694 * Bootstrap Table class
5695 * @cfg {String} cls table class
5696 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5697 * @cfg {String} bgcolor Specifies the background color for a table
5698 * @cfg {Number} border Specifies whether the table cells should have borders or not
5699 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5700 * @cfg {Number} cellspacing Specifies the space between cells
5701 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5702 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5703 * @cfg {String} sortable Specifies that the table should be sortable
5704 * @cfg {String} summary Specifies a summary of the content of a table
5705 * @cfg {Number} width Specifies the width of a table
5706 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5708 * @cfg {boolean} striped Should the rows be alternative striped
5709 * @cfg {boolean} bordered Add borders to the table
5710 * @cfg {boolean} hover Add hover highlighting
5711 * @cfg {boolean} condensed Format condensed
5712 * @cfg {boolean} responsive Format condensed
5713 * @cfg {Boolean} loadMask (true|false) default false
5714 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5715 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5716 * @cfg {Boolean} rowSelection (true|false) default false
5717 * @cfg {Boolean} cellSelection (true|false) default false
5718 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5719 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5723 * Create a new Table
5724 * @param {Object} config The config object
5727 Roo.bootstrap.Table = function(config){
5728 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5733 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5734 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5735 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5736 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5738 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5740 this.sm.grid = this;
5741 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5742 this.sm = this.selModel;
5743 this.sm.xmodule = this.xmodule || false;
5746 if (this.cm && typeof(this.cm.config) == 'undefined') {
5747 this.colModel = new Roo.grid.ColumnModel(this.cm);
5748 this.cm = this.colModel;
5749 this.cm.xmodule = this.xmodule || false;
5752 this.store= Roo.factory(this.store, Roo.data);
5753 this.ds = this.store;
5754 this.ds.xmodule = this.xmodule || false;
5757 if (this.footer && this.store) {
5758 this.footer.dataSource = this.ds;
5759 this.footer = Roo.factory(this.footer);
5766 * Fires when a cell is clicked
5767 * @param {Roo.bootstrap.Table} this
5768 * @param {Roo.Element} el
5769 * @param {Number} rowIndex
5770 * @param {Number} columnIndex
5771 * @param {Roo.EventObject} e
5775 * @event celldblclick
5776 * Fires when a cell is double clicked
5777 * @param {Roo.bootstrap.Table} this
5778 * @param {Roo.Element} el
5779 * @param {Number} rowIndex
5780 * @param {Number} columnIndex
5781 * @param {Roo.EventObject} e
5783 "celldblclick" : true,
5786 * Fires when a row is clicked
5787 * @param {Roo.bootstrap.Table} this
5788 * @param {Roo.Element} el
5789 * @param {Number} rowIndex
5790 * @param {Roo.EventObject} e
5794 * @event rowdblclick
5795 * Fires when a row is double clicked
5796 * @param {Roo.bootstrap.Table} this
5797 * @param {Roo.Element} el
5798 * @param {Number} rowIndex
5799 * @param {Roo.EventObject} e
5801 "rowdblclick" : true,
5804 * Fires when a mouseover occur
5805 * @param {Roo.bootstrap.Table} this
5806 * @param {Roo.Element} el
5807 * @param {Number} rowIndex
5808 * @param {Number} columnIndex
5809 * @param {Roo.EventObject} e
5814 * Fires when a mouseout occur
5815 * @param {Roo.bootstrap.Table} this
5816 * @param {Roo.Element} el
5817 * @param {Number} rowIndex
5818 * @param {Number} columnIndex
5819 * @param {Roo.EventObject} e
5824 * Fires when a row is rendered, so you can change add a style to it.
5825 * @param {Roo.bootstrap.Table} this
5826 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5830 * @event rowsrendered
5831 * Fires when all the rows have been rendered
5832 * @param {Roo.bootstrap.Table} this
5834 'rowsrendered' : true,
5836 * @event contextmenu
5837 * The raw contextmenu event for the entire grid.
5838 * @param {Roo.EventObject} e
5840 "contextmenu" : true,
5842 * @event rowcontextmenu
5843 * Fires when a row is right clicked
5844 * @param {Roo.bootstrap.Table} this
5845 * @param {Number} rowIndex
5846 * @param {Roo.EventObject} e
5848 "rowcontextmenu" : true,
5850 * @event cellcontextmenu
5851 * Fires when a cell is right clicked
5852 * @param {Roo.bootstrap.Table} this
5853 * @param {Number} rowIndex
5854 * @param {Number} cellIndex
5855 * @param {Roo.EventObject} e
5857 "cellcontextmenu" : true,
5859 * @event headercontextmenu
5860 * Fires when a header is right clicked
5861 * @param {Roo.bootstrap.Table} this
5862 * @param {Number} columnIndex
5863 * @param {Roo.EventObject} e
5865 "headercontextmenu" : true
5869 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5895 rowSelection : false,
5896 cellSelection : false,
5899 // Roo.Element - the tbody
5901 // Roo.Element - thead element
5904 container: false, // used by gridpanel...
5906 getAutoCreate : function()
5908 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5915 if (this.scrollBody) {
5916 cfg.cls += ' table-body-fixed';
5919 cfg.cls += ' table-striped';
5923 cfg.cls += ' table-hover';
5925 if (this.bordered) {
5926 cfg.cls += ' table-bordered';
5928 if (this.condensed) {
5929 cfg.cls += ' table-condensed';
5931 if (this.responsive) {
5932 cfg.cls += ' table-responsive';
5936 cfg.cls+= ' ' +this.cls;
5939 // this lot should be simplifed...
5942 cfg.align=this.align;
5945 cfg.bgcolor=this.bgcolor;
5948 cfg.border=this.border;
5950 if (this.cellpadding) {
5951 cfg.cellpadding=this.cellpadding;
5953 if (this.cellspacing) {
5954 cfg.cellspacing=this.cellspacing;
5957 cfg.frame=this.frame;
5960 cfg.rules=this.rules;
5962 if (this.sortable) {
5963 cfg.sortable=this.sortable;
5966 cfg.summary=this.summary;
5969 cfg.width=this.width;
5972 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5975 if(this.store || this.cm){
5976 if(this.headerShow){
5977 cfg.cn.push(this.renderHeader());
5980 cfg.cn.push(this.renderBody());
5982 if(this.footerShow){
5983 cfg.cn.push(this.renderFooter());
5985 // where does this come from?
5986 //cfg.cls+= ' TableGrid';
5989 return { cn : [ cfg ] };
5992 initEvents : function()
5994 if(!this.store || !this.cm){
5997 if (this.selModel) {
5998 this.selModel.initEvents();
6002 //Roo.log('initEvents with ds!!!!');
6004 this.mainBody = this.el.select('tbody', true).first();
6005 this.mainHead = this.el.select('thead', true).first();
6012 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6013 e.on('click', _this.sort, _this);
6016 this.mainBody.on("click", this.onClick, this);
6017 this.mainBody.on("dblclick", this.onDblClick, this);
6019 // why is this done????? = it breaks dialogs??
6020 //this.parent().el.setStyle('position', 'relative');
6024 this.footer.parentId = this.id;
6025 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6028 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6030 this.store.on('load', this.onLoad, this);
6031 this.store.on('beforeload', this.onBeforeLoad, this);
6032 this.store.on('update', this.onUpdate, this);
6033 this.store.on('add', this.onAdd, this);
6034 this.store.on("clear", this.clear, this);
6036 this.el.on("contextmenu", this.onContextMenu, this);
6038 this.mainBody.on('scroll', this.onBodyScroll, this);
6043 onContextMenu : function(e, t)
6045 this.processEvent("contextmenu", e);
6048 processEvent : function(name, e)
6050 if (name != 'touchstart' ) {
6051 this.fireEvent(name, e);
6054 var t = e.getTarget();
6056 var cell = Roo.get(t);
6062 if(cell.findParent('tfoot', false, true)){
6066 if(cell.findParent('thead', false, true)){
6068 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6069 cell = Roo.get(t).findParent('th', false, true);
6071 Roo.log("failed to find th in thead?");
6072 Roo.log(e.getTarget());
6077 var cellIndex = cell.dom.cellIndex;
6079 var ename = name == 'touchstart' ? 'click' : name;
6080 this.fireEvent("header" + ename, this, cellIndex, e);
6085 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6086 cell = Roo.get(t).findParent('td', false, true);
6088 Roo.log("failed to find th in tbody?");
6089 Roo.log(e.getTarget());
6094 var row = cell.findParent('tr', false, true);
6095 var cellIndex = cell.dom.cellIndex;
6096 var rowIndex = row.dom.rowIndex - 1;
6100 this.fireEvent("row" + name, this, rowIndex, e);
6104 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6110 onMouseover : function(e, el)
6112 var cell = Roo.get(el);
6118 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6119 cell = cell.findParent('td', false, true);
6122 var row = cell.findParent('tr', false, true);
6123 var cellIndex = cell.dom.cellIndex;
6124 var rowIndex = row.dom.rowIndex - 1; // start from 0
6126 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6130 onMouseout : function(e, el)
6132 var cell = Roo.get(el);
6138 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6139 cell = cell.findParent('td', false, true);
6142 var row = cell.findParent('tr', false, true);
6143 var cellIndex = cell.dom.cellIndex;
6144 var rowIndex = row.dom.rowIndex - 1; // start from 0
6146 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6150 onClick : function(e, el)
6152 var cell = Roo.get(el);
6154 if(!cell || (!this.cellSelection && !this.rowSelection)){
6158 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6159 cell = cell.findParent('td', false, true);
6162 if(!cell || typeof(cell) == 'undefined'){
6166 var row = cell.findParent('tr', false, true);
6168 if(!row || typeof(row) == 'undefined'){
6172 var cellIndex = cell.dom.cellIndex;
6173 var rowIndex = this.getRowIndex(row);
6175 // why??? - should these not be based on SelectionModel?
6176 if(this.cellSelection){
6177 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6180 if(this.rowSelection){
6181 this.fireEvent('rowclick', this, row, rowIndex, e);
6187 onDblClick : function(e,el)
6189 var cell = Roo.get(el);
6191 if(!cell || (!this.cellSelection && !this.rowSelection)){
6195 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6196 cell = cell.findParent('td', false, true);
6199 if(!cell || typeof(cell) == 'undefined'){
6203 var row = cell.findParent('tr', false, true);
6205 if(!row || typeof(row) == 'undefined'){
6209 var cellIndex = cell.dom.cellIndex;
6210 var rowIndex = this.getRowIndex(row);
6212 if(this.cellSelection){
6213 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6216 if(this.rowSelection){
6217 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6221 sort : function(e,el)
6223 var col = Roo.get(el);
6225 if(!col.hasClass('sortable')){
6229 var sort = col.attr('sort');
6232 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6236 this.store.sortInfo = {field : sort, direction : dir};
6239 Roo.log("calling footer first");
6240 this.footer.onClick('first');
6243 this.store.load({ params : { start : 0 } });
6247 renderHeader : function()
6255 this.totalWidth = 0;
6257 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6259 var config = cm.config[i];
6264 html: cm.getColumnHeader(i)
6269 if(typeof(config.sortable) != 'undefined' && config.sortable){
6271 c.html = '<i class="glyphicon"></i>' + c.html;
6274 if(typeof(config.lgHeader) != 'undefined'){
6275 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6278 if(typeof(config.mdHeader) != 'undefined'){
6279 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6282 if(typeof(config.smHeader) != 'undefined'){
6283 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6286 if(typeof(config.xsHeader) != 'undefined'){
6287 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6294 if(typeof(config.tooltip) != 'undefined'){
6295 c.tooltip = config.tooltip;
6298 if(typeof(config.colspan) != 'undefined'){
6299 c.colspan = config.colspan;
6302 if(typeof(config.hidden) != 'undefined' && config.hidden){
6303 c.style += ' display:none;';
6306 if(typeof(config.dataIndex) != 'undefined'){
6307 c.sort = config.dataIndex;
6312 if(typeof(config.align) != 'undefined' && config.align.length){
6313 c.style += ' text-align:' + config.align + ';';
6316 if(typeof(config.width) != 'undefined'){
6317 c.style += ' width:' + config.width + 'px;';
6318 this.totalWidth += config.width;
6320 this.totalWidth += 100; // assume minimum of 100 per column?
6323 if(typeof(config.cls) != 'undefined'){
6324 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6327 ['xs','sm','md','lg'].map(function(size){
6329 if(typeof(config[size]) == 'undefined'){
6333 if (!config[size]) { // 0 = hidden
6334 c.cls += ' hidden-' + size;
6338 c.cls += ' col-' + size + '-' + config[size];
6348 renderBody : function()
6358 colspan : this.cm.getColumnCount()
6368 renderFooter : function()
6378 colspan : this.cm.getColumnCount()
6392 // Roo.log('ds onload');
6397 var ds = this.store;
6399 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6400 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6401 if (_this.store.sortInfo) {
6403 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6404 e.select('i', true).addClass(['glyphicon-arrow-up']);
6407 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6408 e.select('i', true).addClass(['glyphicon-arrow-down']);
6413 var tbody = this.mainBody;
6415 if(ds.getCount() > 0){
6416 ds.data.each(function(d,rowIndex){
6417 var row = this.renderRow(cm, ds, rowIndex);
6419 tbody.createChild(row);
6423 if(row.cellObjects.length){
6424 Roo.each(row.cellObjects, function(r){
6425 _this.renderCellObject(r);
6432 Roo.each(this.el.select('tbody td', true).elements, function(e){
6433 e.on('mouseover', _this.onMouseover, _this);
6436 Roo.each(this.el.select('tbody td', true).elements, function(e){
6437 e.on('mouseout', _this.onMouseout, _this);
6439 this.fireEvent('rowsrendered', this);
6440 //if(this.loadMask){
6441 // this.maskEl.hide();
6448 onUpdate : function(ds,record)
6450 this.refreshRow(record);
6454 onRemove : function(ds, record, index, isUpdate){
6455 if(isUpdate !== true){
6456 this.fireEvent("beforerowremoved", this, index, record);
6458 var bt = this.mainBody.dom;
6460 var rows = this.el.select('tbody > tr', true).elements;
6462 if(typeof(rows[index]) != 'undefined'){
6463 bt.removeChild(rows[index].dom);
6466 // if(bt.rows[index]){
6467 // bt.removeChild(bt.rows[index]);
6470 if(isUpdate !== true){
6471 //this.stripeRows(index);
6472 //this.syncRowHeights(index, index);
6474 this.fireEvent("rowremoved", this, index, record);
6478 onAdd : function(ds, records, rowIndex)
6480 //Roo.log('on Add called');
6481 // - note this does not handle multiple adding very well..
6482 var bt = this.mainBody.dom;
6483 for (var i =0 ; i < records.length;i++) {
6484 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6485 //Roo.log(records[i]);
6486 //Roo.log(this.store.getAt(rowIndex+i));
6487 this.insertRow(this.store, rowIndex + i, false);
6494 refreshRow : function(record){
6495 var ds = this.store, index;
6496 if(typeof record == 'number'){
6498 record = ds.getAt(index);
6500 index = ds.indexOf(record);
6502 this.insertRow(ds, index, true);
6504 this.onRemove(ds, record, index+1, true);
6506 //this.syncRowHeights(index, index);
6508 this.fireEvent("rowupdated", this, index, record);
6511 insertRow : function(dm, rowIndex, isUpdate){
6514 this.fireEvent("beforerowsinserted", this, rowIndex);
6516 //var s = this.getScrollState();
6517 var row = this.renderRow(this.cm, this.store, rowIndex);
6518 // insert before rowIndex..
6519 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6523 if(row.cellObjects.length){
6524 Roo.each(row.cellObjects, function(r){
6525 _this.renderCellObject(r);
6530 this.fireEvent("rowsinserted", this, rowIndex);
6531 //this.syncRowHeights(firstRow, lastRow);
6532 //this.stripeRows(firstRow);
6539 getRowDom : function(rowIndex)
6541 var rows = this.el.select('tbody > tr', true).elements;
6543 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6546 // returns the object tree for a tr..
6549 renderRow : function(cm, ds, rowIndex)
6552 var d = ds.getAt(rowIndex);
6559 var cellObjects = [];
6561 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6562 var config = cm.config[i];
6564 var renderer = cm.getRenderer(i);
6568 if(typeof(renderer) !== 'undefined'){
6569 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6571 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6572 // and are rendered into the cells after the row is rendered - using the id for the element.
6574 if(typeof(value) === 'object'){
6584 rowIndex : rowIndex,
6589 this.fireEvent('rowclass', this, rowcfg);
6593 cls : rowcfg.rowClass,
6595 html: (typeof(value) === 'object') ? '' : value
6602 if(typeof(config.colspan) != 'undefined'){
6603 td.colspan = config.colspan;
6606 if(typeof(config.hidden) != 'undefined' && config.hidden){
6607 td.style += ' display:none;';
6610 if(typeof(config.align) != 'undefined' && config.align.length){
6611 td.style += ' text-align:' + config.align + ';';
6614 if(typeof(config.width) != 'undefined'){
6615 td.style += ' width:' + config.width + 'px;';
6618 if(typeof(config.cursor) != 'undefined'){
6619 td.style += ' cursor:' + config.cursor + ';';
6622 if(typeof(config.cls) != 'undefined'){
6623 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6626 ['xs','sm','md','lg'].map(function(size){
6628 if(typeof(config[size]) == 'undefined'){
6632 if (!config[size]) { // 0 = hidden
6633 td.cls += ' hidden-' + size;
6637 td.cls += ' col-' + size + '-' + config[size];
6645 row.cellObjects = cellObjects;
6653 onBeforeLoad : function()
6655 //Roo.log('ds onBeforeLoad');
6659 //if(this.loadMask){
6660 // this.maskEl.show();
6668 this.el.select('tbody', true).first().dom.innerHTML = '';
6671 * Show or hide a row.
6672 * @param {Number} rowIndex to show or hide
6673 * @param {Boolean} state hide
6675 setRowVisibility : function(rowIndex, state)
6677 var bt = this.mainBody.dom;
6679 var rows = this.el.select('tbody > tr', true).elements;
6681 if(typeof(rows[rowIndex]) == 'undefined'){
6684 rows[rowIndex].dom.style.display = state ? '' : 'none';
6688 getSelectionModel : function(){
6690 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6692 return this.selModel;
6695 * Render the Roo.bootstrap object from renderder
6697 renderCellObject : function(r)
6701 var t = r.cfg.render(r.container);
6704 Roo.each(r.cfg.cn, function(c){
6706 container: t.getChildContainer(),
6709 _this.renderCellObject(child);
6714 getRowIndex : function(row)
6718 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6729 * Returns the grid's underlying element = used by panel.Grid
6730 * @return {Element} The element
6732 getGridEl : function(){
6736 * Forces a resize - used by panel.Grid
6737 * @return {Element} The element
6739 autoSize : function()
6741 //var ctr = Roo.get(this.container.dom.parentElement);
6742 var ctr = Roo.get(this.el.dom);
6744 var thd = this.getGridEl().select('thead',true).first();
6745 var tbd = this.getGridEl().select('tbody', true).first();
6746 var tfd = this.getGridEl().select('tfoot', true).first();
6748 var cw = ctr.getWidth();
6752 tbd.setSize(ctr.getWidth(),
6753 ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6755 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6758 cw = Math.max(cw, this.totalWidth);
6759 this.getGridEl().select('tr',true).setWidth(cw);
6760 // resize 'expandable coloumn?
6762 return; // we doe not have a view in this design..
6765 onBodyScroll: function()
6768 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6769 this.mainHead.setStyle({
6770 'position' : 'relative',
6771 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6788 * @class Roo.bootstrap.TableCell
6789 * @extends Roo.bootstrap.Component
6790 * Bootstrap TableCell class
6791 * @cfg {String} html cell contain text
6792 * @cfg {String} cls cell class
6793 * @cfg {String} tag cell tag (td|th) default td
6794 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6795 * @cfg {String} align Aligns the content in a cell
6796 * @cfg {String} axis Categorizes cells
6797 * @cfg {String} bgcolor Specifies the background color of a cell
6798 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6799 * @cfg {Number} colspan Specifies the number of columns a cell should span
6800 * @cfg {String} headers Specifies one or more header cells a cell is related to
6801 * @cfg {Number} height Sets the height of a cell
6802 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6803 * @cfg {Number} rowspan Sets the number of rows a cell should span
6804 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6805 * @cfg {String} valign Vertical aligns the content in a cell
6806 * @cfg {Number} width Specifies the width of a cell
6809 * Create a new TableCell
6810 * @param {Object} config The config object
6813 Roo.bootstrap.TableCell = function(config){
6814 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6817 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6837 getAutoCreate : function(){
6838 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6858 cfg.align=this.align
6864 cfg.bgcolor=this.bgcolor
6867 cfg.charoff=this.charoff
6870 cfg.colspan=this.colspan
6873 cfg.headers=this.headers
6876 cfg.height=this.height
6879 cfg.nowrap=this.nowrap
6882 cfg.rowspan=this.rowspan
6885 cfg.scope=this.scope
6888 cfg.valign=this.valign
6891 cfg.width=this.width
6910 * @class Roo.bootstrap.TableRow
6911 * @extends Roo.bootstrap.Component
6912 * Bootstrap TableRow class
6913 * @cfg {String} cls row class
6914 * @cfg {String} align Aligns the content in a table row
6915 * @cfg {String} bgcolor Specifies a background color for a table row
6916 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6917 * @cfg {String} valign Vertical aligns the content in a table row
6920 * Create a new TableRow
6921 * @param {Object} config The config object
6924 Roo.bootstrap.TableRow = function(config){
6925 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6928 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6936 getAutoCreate : function(){
6937 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6947 cfg.align = this.align;
6950 cfg.bgcolor = this.bgcolor;
6953 cfg.charoff = this.charoff;
6956 cfg.valign = this.valign;
6974 * @class Roo.bootstrap.TableBody
6975 * @extends Roo.bootstrap.Component
6976 * Bootstrap TableBody class
6977 * @cfg {String} cls element class
6978 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6979 * @cfg {String} align Aligns the content inside the element
6980 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6981 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6984 * Create a new TableBody
6985 * @param {Object} config The config object
6988 Roo.bootstrap.TableBody = function(config){
6989 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6992 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7000 getAutoCreate : function(){
7001 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7015 cfg.align = this.align;
7018 cfg.charoff = this.charoff;
7021 cfg.valign = this.valign;
7028 // initEvents : function()
7035 // this.store = Roo.factory(this.store, Roo.data);
7036 // this.store.on('load', this.onLoad, this);
7038 // this.store.load();
7042 // onLoad: function ()
7044 // this.fireEvent('load', this);
7054 * Ext JS Library 1.1.1
7055 * Copyright(c) 2006-2007, Ext JS, LLC.
7057 * Originally Released Under LGPL - original licence link has changed is not relivant.
7060 * <script type="text/javascript">
7063 // as we use this in bootstrap.
7064 Roo.namespace('Roo.form');
7066 * @class Roo.form.Action
7067 * Internal Class used to handle form actions
7069 * @param {Roo.form.BasicForm} el The form element or its id
7070 * @param {Object} config Configuration options
7075 // define the action interface
7076 Roo.form.Action = function(form, options){
7078 this.options = options || {};
7081 * Client Validation Failed
7084 Roo.form.Action.CLIENT_INVALID = 'client';
7086 * Server Validation Failed
7089 Roo.form.Action.SERVER_INVALID = 'server';
7091 * Connect to Server Failed
7094 Roo.form.Action.CONNECT_FAILURE = 'connect';
7096 * Reading Data from Server Failed
7099 Roo.form.Action.LOAD_FAILURE = 'load';
7101 Roo.form.Action.prototype = {
7103 failureType : undefined,
7104 response : undefined,
7108 run : function(options){
7113 success : function(response){
7118 handleResponse : function(response){
7122 // default connection failure
7123 failure : function(response){
7125 this.response = response;
7126 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7127 this.form.afterAction(this, false);
7130 processResponse : function(response){
7131 this.response = response;
7132 if(!response.responseText){
7135 this.result = this.handleResponse(response);
7139 // utility functions used internally
7140 getUrl : function(appendParams){
7141 var url = this.options.url || this.form.url || this.form.el.dom.action;
7143 var p = this.getParams();
7145 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7151 getMethod : function(){
7152 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7155 getParams : function(){
7156 var bp = this.form.baseParams;
7157 var p = this.options.params;
7159 if(typeof p == "object"){
7160 p = Roo.urlEncode(Roo.applyIf(p, bp));
7161 }else if(typeof p == 'string' && bp){
7162 p += '&' + Roo.urlEncode(bp);
7165 p = Roo.urlEncode(bp);
7170 createCallback : function(){
7172 success: this.success,
7173 failure: this.failure,
7175 timeout: (this.form.timeout*1000),
7176 upload: this.form.fileUpload ? this.success : undefined
7181 Roo.form.Action.Submit = function(form, options){
7182 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7185 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7188 haveProgress : false,
7189 uploadComplete : false,
7191 // uploadProgress indicator.
7192 uploadProgress : function()
7194 if (!this.form.progressUrl) {
7198 if (!this.haveProgress) {
7199 Roo.MessageBox.progress("Uploading", "Uploading");
7201 if (this.uploadComplete) {
7202 Roo.MessageBox.hide();
7206 this.haveProgress = true;
7208 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7210 var c = new Roo.data.Connection();
7212 url : this.form.progressUrl,
7217 success : function(req){
7218 //console.log(data);
7222 rdata = Roo.decode(req.responseText)
7224 Roo.log("Invalid data from server..");
7228 if (!rdata || !rdata.success) {
7230 Roo.MessageBox.alert(Roo.encode(rdata));
7233 var data = rdata.data;
7235 if (this.uploadComplete) {
7236 Roo.MessageBox.hide();
7241 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7242 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7245 this.uploadProgress.defer(2000,this);
7248 failure: function(data) {
7249 Roo.log('progress url failed ');
7260 // run get Values on the form, so it syncs any secondary forms.
7261 this.form.getValues();
7263 var o = this.options;
7264 var method = this.getMethod();
7265 var isPost = method == 'POST';
7266 if(o.clientValidation === false || this.form.isValid()){
7268 if (this.form.progressUrl) {
7269 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7270 (new Date() * 1) + '' + Math.random());
7275 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7276 form:this.form.el.dom,
7277 url:this.getUrl(!isPost),
7279 params:isPost ? this.getParams() : null,
7280 isUpload: this.form.fileUpload
7283 this.uploadProgress();
7285 }else if (o.clientValidation !== false){ // client validation failed
7286 this.failureType = Roo.form.Action.CLIENT_INVALID;
7287 this.form.afterAction(this, false);
7291 success : function(response)
7293 this.uploadComplete= true;
7294 if (this.haveProgress) {
7295 Roo.MessageBox.hide();
7299 var result = this.processResponse(response);
7300 if(result === true || result.success){
7301 this.form.afterAction(this, true);
7305 this.form.markInvalid(result.errors);
7306 this.failureType = Roo.form.Action.SERVER_INVALID;
7308 this.form.afterAction(this, false);
7310 failure : function(response)
7312 this.uploadComplete= true;
7313 if (this.haveProgress) {
7314 Roo.MessageBox.hide();
7317 this.response = response;
7318 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7319 this.form.afterAction(this, false);
7322 handleResponse : function(response){
7323 if(this.form.errorReader){
7324 var rs = this.form.errorReader.read(response);
7327 for(var i = 0, len = rs.records.length; i < len; i++) {
7328 var r = rs.records[i];
7332 if(errors.length < 1){
7336 success : rs.success,
7342 ret = Roo.decode(response.responseText);
7346 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7356 Roo.form.Action.Load = function(form, options){
7357 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7358 this.reader = this.form.reader;
7361 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7366 Roo.Ajax.request(Roo.apply(
7367 this.createCallback(), {
7368 method:this.getMethod(),
7369 url:this.getUrl(false),
7370 params:this.getParams()
7374 success : function(response){
7376 var result = this.processResponse(response);
7377 if(result === true || !result.success || !result.data){
7378 this.failureType = Roo.form.Action.LOAD_FAILURE;
7379 this.form.afterAction(this, false);
7382 this.form.clearInvalid();
7383 this.form.setValues(result.data);
7384 this.form.afterAction(this, true);
7387 handleResponse : function(response){
7388 if(this.form.reader){
7389 var rs = this.form.reader.read(response);
7390 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7392 success : rs.success,
7396 return Roo.decode(response.responseText);
7400 Roo.form.Action.ACTION_TYPES = {
7401 'load' : Roo.form.Action.Load,
7402 'submit' : Roo.form.Action.Submit
7411 * @class Roo.bootstrap.Form
7412 * @extends Roo.bootstrap.Component
7413 * Bootstrap Form class
7414 * @cfg {String} method GET | POST (default POST)
7415 * @cfg {String} labelAlign top | left (default top)
7416 * @cfg {String} align left | right - for navbars
7417 * @cfg {Boolean} loadMask load mask when submit (default true)
7422 * @param {Object} config The config object
7426 Roo.bootstrap.Form = function(config){
7427 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7430 * @event clientvalidation
7431 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7432 * @param {Form} this
7433 * @param {Boolean} valid true if the form has passed client-side validation
7435 clientvalidation: true,
7437 * @event beforeaction
7438 * Fires before any action is performed. Return false to cancel the action.
7439 * @param {Form} this
7440 * @param {Action} action The action to be performed
7444 * @event actionfailed
7445 * Fires when an action fails.
7446 * @param {Form} this
7447 * @param {Action} action The action that failed
7449 actionfailed : true,
7451 * @event actioncomplete
7452 * Fires when an action is completed.
7453 * @param {Form} this
7454 * @param {Action} action The action that completed
7456 actioncomplete : true
7461 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7464 * @cfg {String} method
7465 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7470 * The URL to use for form actions if one isn't supplied in the action options.
7473 * @cfg {Boolean} fileUpload
7474 * Set to true if this form is a file upload.
7478 * @cfg {Object} baseParams
7479 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7483 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7487 * @cfg {Sting} align (left|right) for navbar forms
7492 activeAction : null,
7495 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7496 * element by passing it or its id or mask the form itself by passing in true.
7499 waitMsgTarget : false,
7503 getAutoCreate : function(){
7507 method : this.method || 'POST',
7508 id : this.id || Roo.id(),
7511 if (this.parent().xtype.match(/^Nav/)) {
7512 cfg.cls = 'navbar-form navbar-' + this.align;
7516 if (this.labelAlign == 'left' ) {
7517 cfg.cls += ' form-horizontal';
7523 initEvents : function()
7525 this.el.on('submit', this.onSubmit, this);
7526 // this was added as random key presses on the form where triggering form submit.
7527 this.el.on('keypress', function(e) {
7528 if (e.getCharCode() != 13) {
7531 // we might need to allow it for textareas.. and some other items.
7532 // check e.getTarget().
7534 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7538 Roo.log("keypress blocked");
7546 onSubmit : function(e){
7551 * Returns true if client-side validation on the form is successful.
7554 isValid : function(){
7555 var items = this.getItems();
7557 items.each(function(f){
7566 * Returns true if any fields in this form have changed since their original load.
7569 isDirty : function(){
7571 var items = this.getItems();
7572 items.each(function(f){
7582 * Performs a predefined action (submit or load) or custom actions you define on this form.
7583 * @param {String} actionName The name of the action type
7584 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7585 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7586 * accept other config options):
7588 Property Type Description
7589 ---------------- --------------- ----------------------------------------------------------------------------------
7590 url String The url for the action (defaults to the form's url)
7591 method String The form method to use (defaults to the form's method, or POST if not defined)
7592 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7593 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7594 validate the form on the client (defaults to false)
7596 * @return {BasicForm} this
7598 doAction : function(action, options){
7599 if(typeof action == 'string'){
7600 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7602 if(this.fireEvent('beforeaction', this, action) !== false){
7603 this.beforeAction(action);
7604 action.run.defer(100, action);
7610 beforeAction : function(action){
7611 var o = action.options;
7614 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7616 // not really supported yet.. ??
7618 //if(this.waitMsgTarget === true){
7619 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7620 //}else if(this.waitMsgTarget){
7621 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7622 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7624 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7630 afterAction : function(action, success){
7631 this.activeAction = null;
7632 var o = action.options;
7634 //if(this.waitMsgTarget === true){
7636 //}else if(this.waitMsgTarget){
7637 // this.waitMsgTarget.unmask();
7639 // Roo.MessageBox.updateProgress(1);
7640 // Roo.MessageBox.hide();
7647 Roo.callback(o.success, o.scope, [this, action]);
7648 this.fireEvent('actioncomplete', this, action);
7652 // failure condition..
7653 // we have a scenario where updates need confirming.
7654 // eg. if a locking scenario exists..
7655 // we look for { errors : { needs_confirm : true }} in the response.
7657 (typeof(action.result) != 'undefined') &&
7658 (typeof(action.result.errors) != 'undefined') &&
7659 (typeof(action.result.errors.needs_confirm) != 'undefined')
7662 Roo.log("not supported yet");
7665 Roo.MessageBox.confirm(
7666 "Change requires confirmation",
7667 action.result.errorMsg,
7672 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7682 Roo.callback(o.failure, o.scope, [this, action]);
7683 // show an error message if no failed handler is set..
7684 if (!this.hasListener('actionfailed')) {
7685 Roo.log("need to add dialog support");
7687 Roo.MessageBox.alert("Error",
7688 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7689 action.result.errorMsg :
7690 "Saving Failed, please check your entries or try again"
7695 this.fireEvent('actionfailed', this, action);
7700 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7701 * @param {String} id The value to search for
7704 findField : function(id){
7705 var items = this.getItems();
7706 var field = items.get(id);
7708 items.each(function(f){
7709 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7716 return field || null;
7719 * Mark fields in this form invalid in bulk.
7720 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7721 * @return {BasicForm} this
7723 markInvalid : function(errors){
7724 if(errors instanceof Array){
7725 for(var i = 0, len = errors.length; i < len; i++){
7726 var fieldError = errors[i];
7727 var f = this.findField(fieldError.id);
7729 f.markInvalid(fieldError.msg);
7735 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7736 field.markInvalid(errors[id]);
7740 //Roo.each(this.childForms || [], function (f) {
7741 // f.markInvalid(errors);
7748 * Set values for fields in this form in bulk.
7749 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7750 * @return {BasicForm} this
7752 setValues : function(values){
7753 if(values instanceof Array){ // array of objects
7754 for(var i = 0, len = values.length; i < len; i++){
7756 var f = this.findField(v.id);
7758 f.setValue(v.value);
7759 if(this.trackResetOnLoad){
7760 f.originalValue = f.getValue();
7764 }else{ // object hash
7767 if(typeof values[id] != 'function' && (field = this.findField(id))){
7769 if (field.setFromData &&
7771 field.displayField &&
7772 // combos' with local stores can
7773 // be queried via setValue()
7774 // to set their value..
7775 (field.store && !field.store.isLocal)
7779 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7780 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7781 field.setFromData(sd);
7784 field.setValue(values[id]);
7788 if(this.trackResetOnLoad){
7789 field.originalValue = field.getValue();
7795 //Roo.each(this.childForms || [], function (f) {
7796 // f.setValues(values);
7803 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7804 * they are returned as an array.
7805 * @param {Boolean} asString
7808 getValues : function(asString){
7809 //if (this.childForms) {
7810 // copy values from the child forms
7811 // Roo.each(this.childForms, function (f) {
7812 // this.setValues(f.getValues());
7818 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7819 if(asString === true){
7822 return Roo.urlDecode(fs);
7826 * Returns the fields in this form as an object with key/value pairs.
7827 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7830 getFieldValues : function(with_hidden)
7832 var items = this.getItems();
7834 items.each(function(f){
7838 var v = f.getValue();
7839 if (f.inputType =='radio') {
7840 if (typeof(ret[f.getName()]) == 'undefined') {
7841 ret[f.getName()] = ''; // empty..
7844 if (!f.el.dom.checked) {
7852 // not sure if this supported any more..
7853 if ((typeof(v) == 'object') && f.getRawValue) {
7854 v = f.getRawValue() ; // dates..
7856 // combo boxes where name != hiddenName...
7857 if (f.name != f.getName()) {
7858 ret[f.name] = f.getRawValue();
7860 ret[f.getName()] = v;
7867 * Clears all invalid messages in this form.
7868 * @return {BasicForm} this
7870 clearInvalid : function(){
7871 var items = this.getItems();
7873 items.each(function(f){
7884 * @return {BasicForm} this
7887 var items = this.getItems();
7888 items.each(function(f){
7892 Roo.each(this.childForms || [], function (f) {
7899 getItems : function()
7901 var r=new Roo.util.MixedCollection(false, function(o){
7902 return o.id || (o.id = Roo.id());
7904 var iter = function(el) {
7911 Roo.each(el.items,function(e) {
7929 * Ext JS Library 1.1.1
7930 * Copyright(c) 2006-2007, Ext JS, LLC.
7932 * Originally Released Under LGPL - original licence link has changed is not relivant.
7935 * <script type="text/javascript">
7938 * @class Roo.form.VTypes
7939 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7942 Roo.form.VTypes = function(){
7943 // closure these in so they are only created once.
7944 var alpha = /^[a-zA-Z_]+$/;
7945 var alphanum = /^[a-zA-Z0-9_]+$/;
7946 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7947 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7949 // All these messages and functions are configurable
7952 * The function used to validate email addresses
7953 * @param {String} value The email address
7955 'email' : function(v){
7956 return email.test(v);
7959 * The error text to display when the email validation function returns false
7962 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7964 * The keystroke filter mask to be applied on email input
7967 'emailMask' : /[a-z0-9_\.\-@]/i,
7970 * The function used to validate URLs
7971 * @param {String} value The URL
7973 'url' : function(v){
7977 * The error text to display when the url validation function returns false
7980 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7983 * The function used to validate alpha values
7984 * @param {String} value The value
7986 'alpha' : function(v){
7987 return alpha.test(v);
7990 * The error text to display when the alpha validation function returns false
7993 'alphaText' : 'This field should only contain letters and _',
7995 * The keystroke filter mask to be applied on alpha input
7998 'alphaMask' : /[a-z_]/i,
8001 * The function used to validate alphanumeric values
8002 * @param {String} value The value
8004 'alphanum' : function(v){
8005 return alphanum.test(v);
8008 * The error text to display when the alphanumeric validation function returns false
8011 'alphanumText' : 'This field should only contain letters, numbers and _',
8013 * The keystroke filter mask to be applied on alphanumeric input
8016 'alphanumMask' : /[a-z0-9_]/i
8026 * @class Roo.bootstrap.Input
8027 * @extends Roo.bootstrap.Component
8028 * Bootstrap Input class
8029 * @cfg {Boolean} disabled is it disabled
8030 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8031 * @cfg {String} name name of the input
8032 * @cfg {string} fieldLabel - the label associated
8033 * @cfg {string} placeholder - placeholder to put in text.
8034 * @cfg {string} before - input group add on before
8035 * @cfg {string} after - input group add on after
8036 * @cfg {string} size - (lg|sm) or leave empty..
8037 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8038 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8039 * @cfg {Number} md colspan out of 12 for computer-sized screens
8040 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8041 * @cfg {string} value default value of the input
8042 * @cfg {Number} labelWidth set the width of label (0-12)
8043 * @cfg {String} labelAlign (top|left)
8044 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8045 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8046 * @cfg {String} indicatorpos (left|right) default left
8048 * @cfg {String} align (left|center|right) Default left
8049 * @cfg {Boolean} forceFeedback (true|false) Default false
8055 * Create a new Input
8056 * @param {Object} config The config object
8059 Roo.bootstrap.Input = function(config){
8060 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8065 * Fires when this field receives input focus.
8066 * @param {Roo.form.Field} this
8071 * Fires when this field loses input focus.
8072 * @param {Roo.form.Field} this
8077 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8078 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8079 * @param {Roo.form.Field} this
8080 * @param {Roo.EventObject} e The event object
8085 * Fires just before the field blurs if the field value has changed.
8086 * @param {Roo.form.Field} this
8087 * @param {Mixed} newValue The new value
8088 * @param {Mixed} oldValue The original value
8093 * Fires after the field has been marked as invalid.
8094 * @param {Roo.form.Field} this
8095 * @param {String} msg The validation message
8100 * Fires after the field has been validated with no errors.
8101 * @param {Roo.form.Field} this
8106 * Fires after the key up
8107 * @param {Roo.form.Field} this
8108 * @param {Roo.EventObject} e The event Object
8114 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8116 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8117 automatic validation (defaults to "keyup").
8119 validationEvent : "keyup",
8121 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8123 validateOnBlur : true,
8125 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8127 validationDelay : 250,
8129 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8131 focusClass : "x-form-focus", // not needed???
8135 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8137 invalidClass : "has-warning",
8140 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8142 validClass : "has-success",
8145 * @cfg {Boolean} hasFeedback (true|false) default true
8150 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8152 invalidFeedbackClass : "glyphicon-warning-sign",
8155 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8157 validFeedbackClass : "glyphicon-ok",
8160 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8162 selectOnFocus : false,
8165 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8169 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8174 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8176 disableKeyFilter : false,
8179 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8183 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8187 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8189 blankText : "This field is required",
8192 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8196 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8198 maxLength : Number.MAX_VALUE,
8200 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8202 minLengthText : "The minimum length for this field is {0}",
8204 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8206 maxLengthText : "The maximum length for this field is {0}",
8210 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8211 * If available, this function will be called only after the basic validators all return true, and will be passed the
8212 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8216 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8217 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8218 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8222 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8226 autocomplete: false,
8245 formatedValue : false,
8246 forceFeedback : false,
8248 indicatorpos : 'left',
8250 parentLabelAlign : function()
8253 while (parent.parent()) {
8254 parent = parent.parent();
8255 if (typeof(parent.labelAlign) !='undefined') {
8256 return parent.labelAlign;
8263 getAutoCreate : function()
8265 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8271 if(this.inputType != 'hidden'){
8272 cfg.cls = 'form-group' //input-group
8278 type : this.inputType,
8280 cls : 'form-control',
8281 placeholder : this.placeholder || '',
8282 autocomplete : this.autocomplete || 'new-password'
8286 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8289 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8290 input.maxLength = this.maxLength;
8293 if (this.disabled) {
8294 input.disabled=true;
8297 if (this.readOnly) {
8298 input.readonly=true;
8302 input.name = this.name;
8306 input.cls += ' input-' + this.size;
8310 ['xs','sm','md','lg'].map(function(size){
8311 if (settings[size]) {
8312 cfg.cls += ' col-' + size + '-' + settings[size];
8316 var inputblock = input;
8320 cls: 'glyphicon form-control-feedback'
8323 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8326 cls : 'has-feedback',
8334 if (this.before || this.after) {
8337 cls : 'input-group',
8341 if (this.before && typeof(this.before) == 'string') {
8343 inputblock.cn.push({
8345 cls : 'roo-input-before input-group-addon',
8349 if (this.before && typeof(this.before) == 'object') {
8350 this.before = Roo.factory(this.before);
8352 inputblock.cn.push({
8354 cls : 'roo-input-before input-group-' +
8355 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8359 inputblock.cn.push(input);
8361 if (this.after && typeof(this.after) == 'string') {
8362 inputblock.cn.push({
8364 cls : 'roo-input-after input-group-addon',
8368 if (this.after && typeof(this.after) == 'object') {
8369 this.after = Roo.factory(this.after);
8371 inputblock.cn.push({
8373 cls : 'roo-input-after input-group-' +
8374 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8378 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8379 inputblock.cls += ' has-feedback';
8380 inputblock.cn.push(feedback);
8384 if (align ==='left' && this.fieldLabel.length) {
8389 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8390 tooltip : 'This field is required'
8395 cls : 'control-label col-sm-' + this.labelWidth,
8396 html : this.fieldLabel
8400 cls : "col-sm-" + (12 - this.labelWidth),
8408 if(this.indicatorpos == 'right'){
8413 cls : 'control-label col-sm-' + this.labelWidth,
8414 html : this.fieldLabel
8419 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8420 tooltip : 'This field is required'
8423 cls : "col-sm-" + (12 - this.labelWidth),
8432 } else if ( this.fieldLabel.length) {
8437 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8438 tooltip : 'This field is required'
8442 //cls : 'input-group-addon',
8443 html : this.fieldLabel
8451 if(this.indicatorpos == 'right'){
8456 //cls : 'input-group-addon',
8457 html : this.fieldLabel
8462 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8463 tooltip : 'This field is required'
8483 if (this.parentType === 'Navbar' && this.parent().bar) {
8484 cfg.cls += ' navbar-form';
8487 if (this.parentType === 'NavGroup') {
8488 cfg.cls += ' navbar-form';
8496 * return the real input element.
8498 inputEl: function ()
8500 return this.el.select('input.form-control',true).first();
8503 tooltipEl : function()
8505 return this.inputEl();
8508 indicatorEl : function()
8510 var indicator = this.el.select('i.roo-required-indicator',true).first();
8520 setDisabled : function(v)
8522 var i = this.inputEl().dom;
8524 i.removeAttribute('disabled');
8528 i.setAttribute('disabled','true');
8530 initEvents : function()
8533 this.inputEl().on("keydown" , this.fireKey, this);
8534 this.inputEl().on("focus", this.onFocus, this);
8535 this.inputEl().on("blur", this.onBlur, this);
8537 this.inputEl().relayEvent('keyup', this);
8539 this.indicator = this.indicatorEl();
8542 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8543 this.indicator.hide();
8546 // reference to original value for reset
8547 this.originalValue = this.getValue();
8548 //Roo.form.TextField.superclass.initEvents.call(this);
8549 if(this.validationEvent == 'keyup'){
8550 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8551 this.inputEl().on('keyup', this.filterValidation, this);
8553 else if(this.validationEvent !== false){
8554 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8557 if(this.selectOnFocus){
8558 this.on("focus", this.preFocus, this);
8561 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8562 this.inputEl().on("keypress", this.filterKeys, this);
8564 this.inputEl().relayEvent('keypress', this);
8567 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8568 this.el.on("click", this.autoSize, this);
8571 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8572 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8575 if (typeof(this.before) == 'object') {
8576 this.before.render(this.el.select('.roo-input-before',true).first());
8578 if (typeof(this.after) == 'object') {
8579 this.after.render(this.el.select('.roo-input-after',true).first());
8584 filterValidation : function(e){
8585 if(!e.isNavKeyPress()){
8586 this.validationTask.delay(this.validationDelay);
8590 * Validates the field value
8591 * @return {Boolean} True if the value is valid, else false
8593 validate : function(){
8594 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8595 if(this.disabled || this.validateValue(this.getRawValue())){
8606 * Validates a value according to the field's validation rules and marks the field as invalid
8607 * if the validation fails
8608 * @param {Mixed} value The value to validate
8609 * @return {Boolean} True if the value is valid, else false
8611 validateValue : function(value){
8612 if(value.length < 1) { // if it's blank
8613 if(this.allowBlank){
8619 if(value.length < this.minLength){
8622 if(value.length > this.maxLength){
8626 var vt = Roo.form.VTypes;
8627 if(!vt[this.vtype](value, this)){
8631 if(typeof this.validator == "function"){
8632 var msg = this.validator(value);
8638 if(this.regex && !this.regex.test(value)){
8648 fireKey : function(e){
8649 //Roo.log('field ' + e.getKey());
8650 if(e.isNavKeyPress()){
8651 this.fireEvent("specialkey", this, e);
8654 focus : function (selectText){
8656 this.inputEl().focus();
8657 if(selectText === true){
8658 this.inputEl().dom.select();
8664 onFocus : function(){
8665 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8666 // this.el.addClass(this.focusClass);
8669 this.hasFocus = true;
8670 this.startValue = this.getValue();
8671 this.fireEvent("focus", this);
8675 beforeBlur : Roo.emptyFn,
8679 onBlur : function(){
8681 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8682 //this.el.removeClass(this.focusClass);
8684 this.hasFocus = false;
8685 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8688 var v = this.getValue();
8689 if(String(v) !== String(this.startValue)){
8690 this.fireEvent('change', this, v, this.startValue);
8692 this.fireEvent("blur", this);
8696 * Resets the current field value to the originally loaded value and clears any validation messages
8699 this.setValue(this.originalValue);
8703 * Returns the name of the field
8704 * @return {Mixed} name The name field
8706 getName: function(){
8710 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8711 * @return {Mixed} value The field value
8713 getValue : function(){
8715 var v = this.inputEl().getValue();
8720 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8721 * @return {Mixed} value The field value
8723 getRawValue : function(){
8724 var v = this.inputEl().getValue();
8730 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8731 * @param {Mixed} value The value to set
8733 setRawValue : function(v){
8734 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8737 selectText : function(start, end){
8738 var v = this.getRawValue();
8740 start = start === undefined ? 0 : start;
8741 end = end === undefined ? v.length : end;
8742 var d = this.inputEl().dom;
8743 if(d.setSelectionRange){
8744 d.setSelectionRange(start, end);
8745 }else if(d.createTextRange){
8746 var range = d.createTextRange();
8747 range.moveStart("character", start);
8748 range.moveEnd("character", v.length-end);
8755 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8756 * @param {Mixed} value The value to set
8758 setValue : function(v){
8761 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8767 processValue : function(value){
8768 if(this.stripCharsRe){
8769 var newValue = value.replace(this.stripCharsRe, '');
8770 if(newValue !== value){
8771 this.setRawValue(newValue);
8778 preFocus : function(){
8780 if(this.selectOnFocus){
8781 this.inputEl().dom.select();
8784 filterKeys : function(e){
8786 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8789 var c = e.getCharCode(), cc = String.fromCharCode(c);
8790 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8793 if(!this.maskRe.test(cc)){
8798 * Clear any invalid styles/messages for this field
8800 clearInvalid : function(){
8802 if(!this.el || this.preventMark){ // not rendered
8807 this.indicator.hide();
8810 this.el.removeClass(this.invalidClass);
8812 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8814 var feedback = this.el.select('.form-control-feedback', true).first();
8817 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8822 this.fireEvent('valid', this);
8826 * Mark this field as valid
8828 markValid : function()
8830 if(!this.el || this.preventMark){ // not rendered
8834 this.el.removeClass([this.invalidClass, this.validClass]);
8836 var feedback = this.el.select('.form-control-feedback', true).first();
8839 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8842 if(this.disabled || this.allowBlank){
8847 this.indicator.hide();
8850 this.el.addClass(this.validClass);
8852 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8854 var feedback = this.el.select('.form-control-feedback', true).first();
8857 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8858 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8863 this.fireEvent('valid', this);
8867 * Mark this field as invalid
8868 * @param {String} msg The validation message
8870 markInvalid : function(msg)
8872 if(!this.el || this.preventMark){ // not rendered
8876 this.el.removeClass([this.invalidClass, this.validClass]);
8878 var feedback = this.el.select('.form-control-feedback', true).first();
8881 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8884 if(this.disabled || this.allowBlank){
8889 this.indicator.show();
8892 this.el.addClass(this.invalidClass);
8894 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8896 var feedback = this.el.select('.form-control-feedback', true).first();
8899 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8901 if(this.getValue().length || this.forceFeedback){
8902 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8909 this.fireEvent('invalid', this, msg);
8912 SafariOnKeyDown : function(event)
8914 // this is a workaround for a password hang bug on chrome/ webkit.
8915 if (this.inputEl().dom.type != 'password') {
8919 var isSelectAll = false;
8921 if(this.inputEl().dom.selectionEnd > 0){
8922 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8924 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8925 event.preventDefault();
8930 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
8932 event.preventDefault();
8933 // this is very hacky as keydown always get's upper case.
8935 var cc = String.fromCharCode(event.getCharCode());
8936 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8940 adjustWidth : function(tag, w){
8941 tag = tag.toLowerCase();
8942 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8943 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8947 if(tag == 'textarea'){
8950 }else if(Roo.isOpera){
8954 if(tag == 'textarea'){
8973 * @class Roo.bootstrap.TextArea
8974 * @extends Roo.bootstrap.Input
8975 * Bootstrap TextArea class
8976 * @cfg {Number} cols Specifies the visible width of a text area
8977 * @cfg {Number} rows Specifies the visible number of lines in a text area
8978 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8979 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8980 * @cfg {string} html text
8983 * Create a new TextArea
8984 * @param {Object} config The config object
8987 Roo.bootstrap.TextArea = function(config){
8988 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8992 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9002 getAutoCreate : function(){
9004 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9015 value : this.value || '',
9016 html: this.html || '',
9017 cls : 'form-control',
9018 placeholder : this.placeholder || ''
9022 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9023 input.maxLength = this.maxLength;
9027 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9031 input.cols = this.cols;
9034 if (this.readOnly) {
9035 input.readonly = true;
9039 input.name = this.name;
9043 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9047 ['xs','sm','md','lg'].map(function(size){
9048 if (settings[size]) {
9049 cfg.cls += ' col-' + size + '-' + settings[size];
9053 var inputblock = input;
9055 if(this.hasFeedback && !this.allowBlank){
9059 cls: 'glyphicon form-control-feedback'
9063 cls : 'has-feedback',
9072 if (this.before || this.after) {
9075 cls : 'input-group',
9079 inputblock.cn.push({
9081 cls : 'input-group-addon',
9086 inputblock.cn.push(input);
9088 if(this.hasFeedback && !this.allowBlank){
9089 inputblock.cls += ' has-feedback';
9090 inputblock.cn.push(feedback);
9094 inputblock.cn.push({
9096 cls : 'input-group-addon',
9103 if (align ==='left' && this.fieldLabel.length) {
9104 // Roo.log("left and has label");
9110 cls : 'control-label col-sm-' + this.labelWidth,
9111 html : this.fieldLabel
9115 cls : "col-sm-" + (12 - this.labelWidth),
9122 } else if ( this.fieldLabel.length) {
9123 // Roo.log(" label");
9128 //cls : 'input-group-addon',
9129 html : this.fieldLabel
9139 // Roo.log(" no label && no align");
9149 if (this.disabled) {
9150 input.disabled=true;
9157 * return the real textarea element.
9159 inputEl: function ()
9161 return this.el.select('textarea.form-control',true).first();
9165 * Clear any invalid styles/messages for this field
9167 clearInvalid : function()
9170 if(!this.el || this.preventMark){ // not rendered
9174 var label = this.el.select('label', true).first();
9175 var icon = this.el.select('i.fa-star', true).first();
9181 this.el.removeClass(this.invalidClass);
9183 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9185 var feedback = this.el.select('.form-control-feedback', true).first();
9188 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9193 this.fireEvent('valid', this);
9197 * Mark this field as valid
9199 markValid : function()
9201 if(!this.el || this.preventMark){ // not rendered
9205 this.el.removeClass([this.invalidClass, this.validClass]);
9207 var feedback = this.el.select('.form-control-feedback', true).first();
9210 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9213 if(this.disabled || this.allowBlank){
9217 var label = this.el.select('label', true).first();
9218 var icon = this.el.select('i.fa-star', true).first();
9224 this.el.addClass(this.validClass);
9226 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9228 var feedback = this.el.select('.form-control-feedback', true).first();
9231 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9232 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9237 this.fireEvent('valid', this);
9241 * Mark this field as invalid
9242 * @param {String} msg The validation message
9244 markInvalid : function(msg)
9246 if(!this.el || this.preventMark){ // not rendered
9250 this.el.removeClass([this.invalidClass, this.validClass]);
9252 var feedback = this.el.select('.form-control-feedback', true).first();
9255 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9258 if(this.disabled || this.allowBlank){
9262 var label = this.el.select('label', true).first();
9263 var icon = this.el.select('i.fa-star', true).first();
9265 if(!this.getValue().length && label && !icon){
9266 this.el.createChild({
9268 cls : 'text-danger fa fa-lg fa-star',
9269 tooltip : 'This field is required',
9270 style : 'margin-right:5px;'
9274 this.el.addClass(this.invalidClass);
9276 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9278 var feedback = this.el.select('.form-control-feedback', true).first();
9281 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9283 if(this.getValue().length || this.forceFeedback){
9284 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9291 this.fireEvent('invalid', this, msg);
9299 * trigger field - base class for combo..
9304 * @class Roo.bootstrap.TriggerField
9305 * @extends Roo.bootstrap.Input
9306 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9307 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9308 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9309 * for which you can provide a custom implementation. For example:
9311 var trigger = new Roo.bootstrap.TriggerField();
9312 trigger.onTriggerClick = myTriggerFn;
9313 trigger.applyTo('my-field');
9316 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9317 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9318 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9319 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9320 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9323 * Create a new TriggerField.
9324 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9325 * to the base TextField)
9327 Roo.bootstrap.TriggerField = function(config){
9328 this.mimicing = false;
9329 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9332 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9334 * @cfg {String} triggerClass A CSS class to apply to the trigger
9337 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9342 * @cfg {Boolean} removable (true|false) special filter default false
9346 /** @cfg {Boolean} grow @hide */
9347 /** @cfg {Number} growMin @hide */
9348 /** @cfg {Number} growMax @hide */
9354 autoSize: Roo.emptyFn,
9361 actionMode : 'wrap',
9366 getAutoCreate : function(){
9368 var align = this.labelAlign || this.parentLabelAlign();
9373 cls: 'form-group' //input-group
9380 type : this.inputType,
9381 cls : 'form-control',
9382 autocomplete: 'new-password',
9383 placeholder : this.placeholder || ''
9387 input.name = this.name;
9390 input.cls += ' input-' + this.size;
9393 if (this.disabled) {
9394 input.disabled=true;
9397 var inputblock = input;
9399 if(this.hasFeedback && !this.allowBlank){
9403 cls: 'glyphicon form-control-feedback'
9406 if(this.removable && !this.editable && !this.tickable){
9408 cls : 'has-feedback',
9414 cls : 'roo-combo-removable-btn close'
9421 cls : 'has-feedback',
9430 if(this.removable && !this.editable && !this.tickable){
9432 cls : 'roo-removable',
9438 cls : 'roo-combo-removable-btn close'
9445 if (this.before || this.after) {
9448 cls : 'input-group',
9452 inputblock.cn.push({
9454 cls : 'input-group-addon',
9459 inputblock.cn.push(input);
9461 if(this.hasFeedback && !this.allowBlank){
9462 inputblock.cls += ' has-feedback';
9463 inputblock.cn.push(feedback);
9467 inputblock.cn.push({
9469 cls : 'input-group-addon',
9482 cls: 'form-hidden-field'
9496 cls: 'form-hidden-field'
9500 cls: 'roo-select2-choices',
9504 cls: 'roo-select2-search-field',
9517 cls: 'roo-select2-container input-group',
9522 // cls: 'typeahead typeahead-long dropdown-menu',
9523 // style: 'display:none'
9528 if(!this.multiple && this.showToggleBtn){
9534 if (this.caret != false) {
9537 cls: 'fa fa-' + this.caret
9544 cls : 'input-group-addon btn dropdown-toggle',
9549 cls: 'combobox-clear',
9563 combobox.cls += ' roo-select2-container-multi';
9566 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9568 // Roo.log("left and has label");
9572 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9573 tooltip : 'This field is required'
9578 cls : 'control-label col-sm-' + this.labelWidth,
9579 html : this.fieldLabel
9583 cls : "col-sm-" + (12 - this.labelWidth),
9591 if(this.indicatorpos == 'right'){
9596 cls : 'control-label col-sm-' + this.labelWidth,
9597 html : this.fieldLabel
9602 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9603 tooltip : 'This field is required'
9606 cls : "col-sm-" + (12 - this.labelWidth),
9615 } else if ( this.fieldLabel.length) {
9616 // Roo.log(" label");
9620 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9621 tooltip : 'This field is required'
9625 //cls : 'input-group-addon',
9626 html : this.fieldLabel
9634 if(this.indicatorpos == 'right'){
9639 //cls : 'input-group-addon',
9640 html : this.fieldLabel
9645 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9646 tooltip : 'This field is required'
9657 // Roo.log(" no label && no align");
9664 ['xs','sm','md','lg'].map(function(size){
9665 if (settings[size]) {
9666 cfg.cls += ' col-' + size + '-' + settings[size];
9677 onResize : function(w, h){
9678 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9679 // if(typeof w == 'number'){
9680 // var x = w - this.trigger.getWidth();
9681 // this.inputEl().setWidth(this.adjustWidth('input', x));
9682 // this.trigger.setStyle('left', x+'px');
9687 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9690 getResizeEl : function(){
9691 return this.inputEl();
9695 getPositionEl : function(){
9696 return this.inputEl();
9700 alignErrorIcon : function(){
9701 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9705 initEvents : function(){
9709 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9710 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9711 if(!this.multiple && this.showToggleBtn){
9712 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9713 if(this.hideTrigger){
9714 this.trigger.setDisplayed(false);
9716 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9720 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9723 if(this.removable && !this.editable && !this.tickable){
9724 var close = this.closeTriggerEl();
9727 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9728 close.on('click', this.removeBtnClick, this, close);
9732 //this.trigger.addClassOnOver('x-form-trigger-over');
9733 //this.trigger.addClassOnClick('x-form-trigger-click');
9736 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9740 closeTriggerEl : function()
9742 var close = this.el.select('.roo-combo-removable-btn', true).first();
9743 return close ? close : false;
9746 removeBtnClick : function(e, h, el)
9750 if(this.fireEvent("remove", this) !== false){
9752 this.fireEvent("afterremove", this)
9756 createList : function()
9758 this.list = Roo.get(document.body).createChild({
9760 cls: 'typeahead typeahead-long dropdown-menu',
9761 style: 'display:none'
9764 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9769 initTrigger : function(){
9774 onDestroy : function(){
9776 this.trigger.removeAllListeners();
9777 // this.trigger.remove();
9780 // this.wrap.remove();
9782 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9786 onFocus : function(){
9787 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9790 this.wrap.addClass('x-trigger-wrap-focus');
9791 this.mimicing = true;
9792 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9793 if(this.monitorTab){
9794 this.el.on("keydown", this.checkTab, this);
9801 checkTab : function(e){
9802 if(e.getKey() == e.TAB){
9808 onBlur : function(){
9813 mimicBlur : function(e, t){
9815 if(!this.wrap.contains(t) && this.validateBlur()){
9822 triggerBlur : function(){
9823 this.mimicing = false;
9824 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9825 if(this.monitorTab){
9826 this.el.un("keydown", this.checkTab, this);
9828 //this.wrap.removeClass('x-trigger-wrap-focus');
9829 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9833 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9834 validateBlur : function(e, t){
9839 onDisable : function(){
9840 this.inputEl().dom.disabled = true;
9841 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9843 // this.wrap.addClass('x-item-disabled');
9848 onEnable : function(){
9849 this.inputEl().dom.disabled = false;
9850 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9852 // this.el.removeClass('x-item-disabled');
9857 onShow : function(){
9858 var ae = this.getActionEl();
9861 ae.dom.style.display = '';
9862 ae.dom.style.visibility = 'visible';
9868 onHide : function(){
9869 var ae = this.getActionEl();
9870 ae.dom.style.display = 'none';
9874 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9875 * by an implementing function.
9877 * @param {EventObject} e
9879 onTriggerClick : Roo.emptyFn
9883 * Ext JS Library 1.1.1
9884 * Copyright(c) 2006-2007, Ext JS, LLC.
9886 * Originally Released Under LGPL - original licence link has changed is not relivant.
9889 * <script type="text/javascript">
9894 * @class Roo.data.SortTypes
9896 * Defines the default sorting (casting?) comparison functions used when sorting data.
9898 Roo.data.SortTypes = {
9900 * Default sort that does nothing
9901 * @param {Mixed} s The value being converted
9902 * @return {Mixed} The comparison value
9909 * The regular expression used to strip tags
9913 stripTagsRE : /<\/?[^>]+>/gi,
9916 * Strips all HTML tags to sort on text only
9917 * @param {Mixed} s The value being converted
9918 * @return {String} The comparison value
9920 asText : function(s){
9921 return String(s).replace(this.stripTagsRE, "");
9925 * Strips all HTML tags to sort on text only - Case insensitive
9926 * @param {Mixed} s The value being converted
9927 * @return {String} The comparison value
9929 asUCText : function(s){
9930 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9934 * Case insensitive string
9935 * @param {Mixed} s The value being converted
9936 * @return {String} The comparison value
9938 asUCString : function(s) {
9939 return String(s).toUpperCase();
9944 * @param {Mixed} s The value being converted
9945 * @return {Number} The comparison value
9947 asDate : function(s) {
9951 if(s instanceof Date){
9954 return Date.parse(String(s));
9959 * @param {Mixed} s The value being converted
9960 * @return {Float} The comparison value
9962 asFloat : function(s) {
9963 var val = parseFloat(String(s).replace(/,/g, ""));
9972 * @param {Mixed} s The value being converted
9973 * @return {Number} The comparison value
9975 asInt : function(s) {
9976 var val = parseInt(String(s).replace(/,/g, ""));
9984 * Ext JS Library 1.1.1
9985 * Copyright(c) 2006-2007, Ext JS, LLC.
9987 * Originally Released Under LGPL - original licence link has changed is not relivant.
9990 * <script type="text/javascript">
9994 * @class Roo.data.Record
9995 * Instances of this class encapsulate both record <em>definition</em> information, and record
9996 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9997 * to access Records cached in an {@link Roo.data.Store} object.<br>
9999 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10000 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10003 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10005 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10006 * {@link #create}. The parameters are the same.
10007 * @param {Array} data An associative Array of data values keyed by the field name.
10008 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10009 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10010 * not specified an integer id is generated.
10012 Roo.data.Record = function(data, id){
10013 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10018 * Generate a constructor for a specific record layout.
10019 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10020 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10021 * Each field definition object may contain the following properties: <ul>
10022 * <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,
10023 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10024 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10025 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10026 * is being used, then this is a string containing the javascript expression to reference the data relative to
10027 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10028 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10029 * this may be omitted.</p></li>
10030 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10031 * <ul><li>auto (Default, implies no conversion)</li>
10036 * <li>date</li></ul></p></li>
10037 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10038 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10039 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10040 * by the Reader into an object that will be stored in the Record. It is passed the
10041 * following parameters:<ul>
10042 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10044 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10046 * <br>usage:<br><pre><code>
10047 var TopicRecord = Roo.data.Record.create(
10048 {name: 'title', mapping: 'topic_title'},
10049 {name: 'author', mapping: 'username'},
10050 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10051 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10052 {name: 'lastPoster', mapping: 'user2'},
10053 {name: 'excerpt', mapping: 'post_text'}
10056 var myNewRecord = new TopicRecord({
10057 title: 'Do my job please',
10060 lastPost: new Date(),
10061 lastPoster: 'Animal',
10062 excerpt: 'No way dude!'
10064 myStore.add(myNewRecord);
10069 Roo.data.Record.create = function(o){
10070 var f = function(){
10071 f.superclass.constructor.apply(this, arguments);
10073 Roo.extend(f, Roo.data.Record);
10074 var p = f.prototype;
10075 p.fields = new Roo.util.MixedCollection(false, function(field){
10078 for(var i = 0, len = o.length; i < len; i++){
10079 p.fields.add(new Roo.data.Field(o[i]));
10081 f.getField = function(name){
10082 return p.fields.get(name);
10087 Roo.data.Record.AUTO_ID = 1000;
10088 Roo.data.Record.EDIT = 'edit';
10089 Roo.data.Record.REJECT = 'reject';
10090 Roo.data.Record.COMMIT = 'commit';
10092 Roo.data.Record.prototype = {
10094 * Readonly flag - true if this record has been modified.
10103 join : function(store){
10104 this.store = store;
10108 * Set the named field to the specified value.
10109 * @param {String} name The name of the field to set.
10110 * @param {Object} value The value to set the field to.
10112 set : function(name, value){
10113 if(this.data[name] == value){
10117 if(!this.modified){
10118 this.modified = {};
10120 if(typeof this.modified[name] == 'undefined'){
10121 this.modified[name] = this.data[name];
10123 this.data[name] = value;
10124 if(!this.editing && this.store){
10125 this.store.afterEdit(this);
10130 * Get the value of the named field.
10131 * @param {String} name The name of the field to get the value of.
10132 * @return {Object} The value of the field.
10134 get : function(name){
10135 return this.data[name];
10139 beginEdit : function(){
10140 this.editing = true;
10141 this.modified = {};
10145 cancelEdit : function(){
10146 this.editing = false;
10147 delete this.modified;
10151 endEdit : function(){
10152 this.editing = false;
10153 if(this.dirty && this.store){
10154 this.store.afterEdit(this);
10159 * Usually called by the {@link Roo.data.Store} which owns the Record.
10160 * Rejects all changes made to the Record since either creation, or the last commit operation.
10161 * Modified fields are reverted to their original values.
10163 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10164 * of reject operations.
10166 reject : function(){
10167 var m = this.modified;
10169 if(typeof m[n] != "function"){
10170 this.data[n] = m[n];
10173 this.dirty = false;
10174 delete this.modified;
10175 this.editing = false;
10177 this.store.afterReject(this);
10182 * Usually called by the {@link Roo.data.Store} which owns the Record.
10183 * Commits all changes made to the Record since either creation, or the last commit operation.
10185 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10186 * of commit operations.
10188 commit : function(){
10189 this.dirty = false;
10190 delete this.modified;
10191 this.editing = false;
10193 this.store.afterCommit(this);
10198 hasError : function(){
10199 return this.error != null;
10203 clearError : function(){
10208 * Creates a copy of this record.
10209 * @param {String} id (optional) A new record id if you don't want to use this record's id
10212 copy : function(newId) {
10213 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10217 * Ext JS Library 1.1.1
10218 * Copyright(c) 2006-2007, Ext JS, LLC.
10220 * Originally Released Under LGPL - original licence link has changed is not relivant.
10223 * <script type="text/javascript">
10229 * @class Roo.data.Store
10230 * @extends Roo.util.Observable
10231 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10232 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10234 * 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
10235 * has no knowledge of the format of the data returned by the Proxy.<br>
10237 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10238 * instances from the data object. These records are cached and made available through accessor functions.
10240 * Creates a new Store.
10241 * @param {Object} config A config object containing the objects needed for the Store to access data,
10242 * and read the data into Records.
10244 Roo.data.Store = function(config){
10245 this.data = new Roo.util.MixedCollection(false);
10246 this.data.getKey = function(o){
10249 this.baseParams = {};
10251 this.paramNames = {
10256 "multisort" : "_multisort"
10259 if(config && config.data){
10260 this.inlineData = config.data;
10261 delete config.data;
10264 Roo.apply(this, config);
10266 if(this.reader){ // reader passed
10267 this.reader = Roo.factory(this.reader, Roo.data);
10268 this.reader.xmodule = this.xmodule || false;
10269 if(!this.recordType){
10270 this.recordType = this.reader.recordType;
10272 if(this.reader.onMetaChange){
10273 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10277 if(this.recordType){
10278 this.fields = this.recordType.prototype.fields;
10280 this.modified = [];
10284 * @event datachanged
10285 * Fires when the data cache has changed, and a widget which is using this Store
10286 * as a Record cache should refresh its view.
10287 * @param {Store} this
10289 datachanged : true,
10291 * @event metachange
10292 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10293 * @param {Store} this
10294 * @param {Object} meta The JSON metadata
10299 * Fires when Records have been added to the Store
10300 * @param {Store} this
10301 * @param {Roo.data.Record[]} records The array of Records added
10302 * @param {Number} index The index at which the record(s) were added
10307 * Fires when a Record has been removed from the Store
10308 * @param {Store} this
10309 * @param {Roo.data.Record} record The Record that was removed
10310 * @param {Number} index The index at which the record was removed
10315 * Fires when a Record has been updated
10316 * @param {Store} this
10317 * @param {Roo.data.Record} record The Record that was updated
10318 * @param {String} operation The update operation being performed. Value may be one of:
10320 Roo.data.Record.EDIT
10321 Roo.data.Record.REJECT
10322 Roo.data.Record.COMMIT
10328 * Fires when the data cache has been cleared.
10329 * @param {Store} this
10333 * @event beforeload
10334 * Fires before a request is made for a new data object. If the beforeload handler returns false
10335 * the load action will be canceled.
10336 * @param {Store} this
10337 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10341 * @event beforeloadadd
10342 * Fires after a new set of Records has been loaded.
10343 * @param {Store} this
10344 * @param {Roo.data.Record[]} records The Records that were loaded
10345 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10347 beforeloadadd : true,
10350 * Fires after a new set of Records has been loaded, before they are added to the store.
10351 * @param {Store} this
10352 * @param {Roo.data.Record[]} records The Records that were loaded
10353 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10354 * @params {Object} return from reader
10358 * @event loadexception
10359 * Fires if an exception occurs in the Proxy during loading.
10360 * Called with the signature of the Proxy's "loadexception" event.
10361 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10364 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10365 * @param {Object} load options
10366 * @param {Object} jsonData from your request (normally this contains the Exception)
10368 loadexception : true
10372 this.proxy = Roo.factory(this.proxy, Roo.data);
10373 this.proxy.xmodule = this.xmodule || false;
10374 this.relayEvents(this.proxy, ["loadexception"]);
10376 this.sortToggle = {};
10377 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10379 Roo.data.Store.superclass.constructor.call(this);
10381 if(this.inlineData){
10382 this.loadData(this.inlineData);
10383 delete this.inlineData;
10387 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10389 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10390 * without a remote query - used by combo/forms at present.
10394 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10397 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10400 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10401 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10404 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10405 * on any HTTP request
10408 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10411 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10415 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10416 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10418 remoteSort : false,
10421 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10422 * loaded or when a record is removed. (defaults to false).
10424 pruneModifiedRecords : false,
10427 lastOptions : null,
10430 * Add Records to the Store and fires the add event.
10431 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10433 add : function(records){
10434 records = [].concat(records);
10435 for(var i = 0, len = records.length; i < len; i++){
10436 records[i].join(this);
10438 var index = this.data.length;
10439 this.data.addAll(records);
10440 this.fireEvent("add", this, records, index);
10444 * Remove a Record from the Store and fires the remove event.
10445 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10447 remove : function(record){
10448 var index = this.data.indexOf(record);
10449 this.data.removeAt(index);
10450 if(this.pruneModifiedRecords){
10451 this.modified.remove(record);
10453 this.fireEvent("remove", this, record, index);
10457 * Remove all Records from the Store and fires the clear event.
10459 removeAll : function(){
10461 if(this.pruneModifiedRecords){
10462 this.modified = [];
10464 this.fireEvent("clear", this);
10468 * Inserts Records to the Store at the given index and fires the add event.
10469 * @param {Number} index The start index at which to insert the passed Records.
10470 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10472 insert : function(index, records){
10473 records = [].concat(records);
10474 for(var i = 0, len = records.length; i < len; i++){
10475 this.data.insert(index, records[i]);
10476 records[i].join(this);
10478 this.fireEvent("add", this, records, index);
10482 * Get the index within the cache of the passed Record.
10483 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10484 * @return {Number} The index of the passed Record. Returns -1 if not found.
10486 indexOf : function(record){
10487 return this.data.indexOf(record);
10491 * Get the index within the cache of the Record with the passed id.
10492 * @param {String} id The id of the Record to find.
10493 * @return {Number} The index of the Record. Returns -1 if not found.
10495 indexOfId : function(id){
10496 return this.data.indexOfKey(id);
10500 * Get the Record with the specified id.
10501 * @param {String} id The id of the Record to find.
10502 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10504 getById : function(id){
10505 return this.data.key(id);
10509 * Get the Record at the specified index.
10510 * @param {Number} index The index of the Record to find.
10511 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10513 getAt : function(index){
10514 return this.data.itemAt(index);
10518 * Returns a range of Records between specified indices.
10519 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10520 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10521 * @return {Roo.data.Record[]} An array of Records
10523 getRange : function(start, end){
10524 return this.data.getRange(start, end);
10528 storeOptions : function(o){
10529 o = Roo.apply({}, o);
10532 this.lastOptions = o;
10536 * Loads the Record cache from the configured Proxy using the configured Reader.
10538 * If using remote paging, then the first load call must specify the <em>start</em>
10539 * and <em>limit</em> properties in the options.params property to establish the initial
10540 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10542 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10543 * and this call will return before the new data has been loaded. Perform any post-processing
10544 * in a callback function, or in a "load" event handler.</strong>
10546 * @param {Object} options An object containing properties which control loading options:<ul>
10547 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10548 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10549 * passed the following arguments:<ul>
10550 * <li>r : Roo.data.Record[]</li>
10551 * <li>options: Options object from the load call</li>
10552 * <li>success: Boolean success indicator</li></ul></li>
10553 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10554 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10557 load : function(options){
10558 options = options || {};
10559 if(this.fireEvent("beforeload", this, options) !== false){
10560 this.storeOptions(options);
10561 var p = Roo.apply(options.params || {}, this.baseParams);
10562 // if meta was not loaded from remote source.. try requesting it.
10563 if (!this.reader.metaFromRemote) {
10564 p._requestMeta = 1;
10566 if(this.sortInfo && this.remoteSort){
10567 var pn = this.paramNames;
10568 p[pn["sort"]] = this.sortInfo.field;
10569 p[pn["dir"]] = this.sortInfo.direction;
10571 if (this.multiSort) {
10572 var pn = this.paramNames;
10573 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10576 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10581 * Reloads the Record cache from the configured Proxy using the configured Reader and
10582 * the options from the last load operation performed.
10583 * @param {Object} options (optional) An object containing properties which may override the options
10584 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10585 * the most recently used options are reused).
10587 reload : function(options){
10588 this.load(Roo.applyIf(options||{}, this.lastOptions));
10592 // Called as a callback by the Reader during a load operation.
10593 loadRecords : function(o, options, success){
10594 if(!o || success === false){
10595 if(success !== false){
10596 this.fireEvent("load", this, [], options, o);
10598 if(options.callback){
10599 options.callback.call(options.scope || this, [], options, false);
10603 // if data returned failure - throw an exception.
10604 if (o.success === false) {
10605 // show a message if no listener is registered.
10606 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10607 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10609 // loadmask wil be hooked into this..
10610 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10613 var r = o.records, t = o.totalRecords || r.length;
10615 this.fireEvent("beforeloadadd", this, r, options, o);
10617 if(!options || options.add !== true){
10618 if(this.pruneModifiedRecords){
10619 this.modified = [];
10621 for(var i = 0, len = r.length; i < len; i++){
10625 this.data = this.snapshot;
10626 delete this.snapshot;
10629 this.data.addAll(r);
10630 this.totalLength = t;
10632 this.fireEvent("datachanged", this);
10634 this.totalLength = Math.max(t, this.data.length+r.length);
10637 this.fireEvent("load", this, r, options, o);
10638 if(options.callback){
10639 options.callback.call(options.scope || this, r, options, true);
10645 * Loads data from a passed data block. A Reader which understands the format of the data
10646 * must have been configured in the constructor.
10647 * @param {Object} data The data block from which to read the Records. The format of the data expected
10648 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10649 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10651 loadData : function(o, append){
10652 var r = this.reader.readRecords(o);
10653 this.loadRecords(r, {add: append}, true);
10657 * Gets the number of cached records.
10659 * <em>If using paging, this may not be the total size of the dataset. If the data object
10660 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10661 * the data set size</em>
10663 getCount : function(){
10664 return this.data.length || 0;
10668 * Gets the total number of records in the dataset as returned by the server.
10670 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10671 * the dataset size</em>
10673 getTotalCount : function(){
10674 return this.totalLength || 0;
10678 * Returns the sort state of the Store as an object with two properties:
10680 field {String} The name of the field by which the Records are sorted
10681 direction {String} The sort order, "ASC" or "DESC"
10684 getSortState : function(){
10685 return this.sortInfo;
10689 applySort : function(){
10690 if(this.sortInfo && !this.remoteSort){
10691 var s = this.sortInfo, f = s.field;
10692 var st = this.fields.get(f).sortType;
10693 var fn = function(r1, r2){
10694 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10695 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10697 this.data.sort(s.direction, fn);
10698 if(this.snapshot && this.snapshot != this.data){
10699 this.snapshot.sort(s.direction, fn);
10705 * Sets the default sort column and order to be used by the next load operation.
10706 * @param {String} fieldName The name of the field to sort by.
10707 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10709 setDefaultSort : function(field, dir){
10710 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10714 * Sort the Records.
10715 * If remote sorting is used, the sort is performed on the server, and the cache is
10716 * reloaded. If local sorting is used, the cache is sorted internally.
10717 * @param {String} fieldName The name of the field to sort by.
10718 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10720 sort : function(fieldName, dir){
10721 var f = this.fields.get(fieldName);
10723 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10725 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10726 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10731 this.sortToggle[f.name] = dir;
10732 this.sortInfo = {field: f.name, direction: dir};
10733 if(!this.remoteSort){
10735 this.fireEvent("datachanged", this);
10737 this.load(this.lastOptions);
10742 * Calls the specified function for each of the Records in the cache.
10743 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10744 * Returning <em>false</em> aborts and exits the iteration.
10745 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10747 each : function(fn, scope){
10748 this.data.each(fn, scope);
10752 * Gets all records modified since the last commit. Modified records are persisted across load operations
10753 * (e.g., during paging).
10754 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10756 getModifiedRecords : function(){
10757 return this.modified;
10761 createFilterFn : function(property, value, anyMatch){
10762 if(!value.exec){ // not a regex
10763 value = String(value);
10764 if(value.length == 0){
10767 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10769 return function(r){
10770 return value.test(r.data[property]);
10775 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10776 * @param {String} property A field on your records
10777 * @param {Number} start The record index to start at (defaults to 0)
10778 * @param {Number} end The last record index to include (defaults to length - 1)
10779 * @return {Number} The sum
10781 sum : function(property, start, end){
10782 var rs = this.data.items, v = 0;
10783 start = start || 0;
10784 end = (end || end === 0) ? end : rs.length-1;
10786 for(var i = start; i <= end; i++){
10787 v += (rs[i].data[property] || 0);
10793 * Filter the records by a specified property.
10794 * @param {String} field A field on your records
10795 * @param {String/RegExp} value Either a string that the field
10796 * should start with or a RegExp to test against the field
10797 * @param {Boolean} anyMatch True to match any part not just the beginning
10799 filter : function(property, value, anyMatch){
10800 var fn = this.createFilterFn(property, value, anyMatch);
10801 return fn ? this.filterBy(fn) : this.clearFilter();
10805 * Filter by a function. The specified function will be called with each
10806 * record in this data source. If the function returns true the record is included,
10807 * otherwise it is filtered.
10808 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10809 * @param {Object} scope (optional) The scope of the function (defaults to this)
10811 filterBy : function(fn, scope){
10812 this.snapshot = this.snapshot || this.data;
10813 this.data = this.queryBy(fn, scope||this);
10814 this.fireEvent("datachanged", this);
10818 * Query the records by a specified property.
10819 * @param {String} field A field on your records
10820 * @param {String/RegExp} value Either a string that the field
10821 * should start with or a RegExp to test against the field
10822 * @param {Boolean} anyMatch True to match any part not just the beginning
10823 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10825 query : function(property, value, anyMatch){
10826 var fn = this.createFilterFn(property, value, anyMatch);
10827 return fn ? this.queryBy(fn) : this.data.clone();
10831 * Query by a function. The specified function will be called with each
10832 * record in this data source. If the function returns true the record is included
10834 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10835 * @param {Object} scope (optional) The scope of the function (defaults to this)
10836 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10838 queryBy : function(fn, scope){
10839 var data = this.snapshot || this.data;
10840 return data.filterBy(fn, scope||this);
10844 * Collects unique values for a particular dataIndex from this store.
10845 * @param {String} dataIndex The property to collect
10846 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10847 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10848 * @return {Array} An array of the unique values
10850 collect : function(dataIndex, allowNull, bypassFilter){
10851 var d = (bypassFilter === true && this.snapshot) ?
10852 this.snapshot.items : this.data.items;
10853 var v, sv, r = [], l = {};
10854 for(var i = 0, len = d.length; i < len; i++){
10855 v = d[i].data[dataIndex];
10857 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10866 * Revert to a view of the Record cache with no filtering applied.
10867 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10869 clearFilter : function(suppressEvent){
10870 if(this.snapshot && this.snapshot != this.data){
10871 this.data = this.snapshot;
10872 delete this.snapshot;
10873 if(suppressEvent !== true){
10874 this.fireEvent("datachanged", this);
10880 afterEdit : function(record){
10881 if(this.modified.indexOf(record) == -1){
10882 this.modified.push(record);
10884 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10888 afterReject : function(record){
10889 this.modified.remove(record);
10890 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10894 afterCommit : function(record){
10895 this.modified.remove(record);
10896 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10900 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10901 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10903 commitChanges : function(){
10904 var m = this.modified.slice(0);
10905 this.modified = [];
10906 for(var i = 0, len = m.length; i < len; i++){
10912 * Cancel outstanding changes on all changed records.
10914 rejectChanges : function(){
10915 var m = this.modified.slice(0);
10916 this.modified = [];
10917 for(var i = 0, len = m.length; i < len; i++){
10922 onMetaChange : function(meta, rtype, o){
10923 this.recordType = rtype;
10924 this.fields = rtype.prototype.fields;
10925 delete this.snapshot;
10926 this.sortInfo = meta.sortInfo || this.sortInfo;
10927 this.modified = [];
10928 this.fireEvent('metachange', this, this.reader.meta);
10931 moveIndex : function(data, type)
10933 var index = this.indexOf(data);
10935 var newIndex = index + type;
10939 this.insert(newIndex, data);
10944 * Ext JS Library 1.1.1
10945 * Copyright(c) 2006-2007, Ext JS, LLC.
10947 * Originally Released Under LGPL - original licence link has changed is not relivant.
10950 * <script type="text/javascript">
10954 * @class Roo.data.SimpleStore
10955 * @extends Roo.data.Store
10956 * Small helper class to make creating Stores from Array data easier.
10957 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10958 * @cfg {Array} fields An array of field definition objects, or field name strings.
10959 * @cfg {Array} data The multi-dimensional array of data
10961 * @param {Object} config
10963 Roo.data.SimpleStore = function(config){
10964 Roo.data.SimpleStore.superclass.constructor.call(this, {
10966 reader: new Roo.data.ArrayReader({
10969 Roo.data.Record.create(config.fields)
10971 proxy : new Roo.data.MemoryProxy(config.data)
10975 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10977 * Ext JS Library 1.1.1
10978 * Copyright(c) 2006-2007, Ext JS, LLC.
10980 * Originally Released Under LGPL - original licence link has changed is not relivant.
10983 * <script type="text/javascript">
10988 * @extends Roo.data.Store
10989 * @class Roo.data.JsonStore
10990 * Small helper class to make creating Stores for JSON data easier. <br/>
10992 var store = new Roo.data.JsonStore({
10993 url: 'get-images.php',
10995 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10998 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10999 * JsonReader and HttpProxy (unless inline data is provided).</b>
11000 * @cfg {Array} fields An array of field definition objects, or field name strings.
11002 * @param {Object} config
11004 Roo.data.JsonStore = function(c){
11005 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11006 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11007 reader: new Roo.data.JsonReader(c, c.fields)
11010 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11012 * Ext JS Library 1.1.1
11013 * Copyright(c) 2006-2007, Ext JS, LLC.
11015 * Originally Released Under LGPL - original licence link has changed is not relivant.
11018 * <script type="text/javascript">
11022 Roo.data.Field = function(config){
11023 if(typeof config == "string"){
11024 config = {name: config};
11026 Roo.apply(this, config);
11029 this.type = "auto";
11032 var st = Roo.data.SortTypes;
11033 // named sortTypes are supported, here we look them up
11034 if(typeof this.sortType == "string"){
11035 this.sortType = st[this.sortType];
11038 // set default sortType for strings and dates
11039 if(!this.sortType){
11042 this.sortType = st.asUCString;
11045 this.sortType = st.asDate;
11048 this.sortType = st.none;
11053 var stripRe = /[\$,%]/g;
11055 // prebuilt conversion function for this field, instead of
11056 // switching every time we're reading a value
11058 var cv, dateFormat = this.dateFormat;
11063 cv = function(v){ return v; };
11066 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11070 return v !== undefined && v !== null && v !== '' ?
11071 parseInt(String(v).replace(stripRe, ""), 10) : '';
11076 return v !== undefined && v !== null && v !== '' ?
11077 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11082 cv = function(v){ return v === true || v === "true" || v == 1; };
11089 if(v instanceof Date){
11093 if(dateFormat == "timestamp"){
11094 return new Date(v*1000);
11096 return Date.parseDate(v, dateFormat);
11098 var parsed = Date.parse(v);
11099 return parsed ? new Date(parsed) : null;
11108 Roo.data.Field.prototype = {
11116 * Ext JS Library 1.1.1
11117 * Copyright(c) 2006-2007, Ext JS, LLC.
11119 * Originally Released Under LGPL - original licence link has changed is not relivant.
11122 * <script type="text/javascript">
11125 // Base class for reading structured data from a data source. This class is intended to be
11126 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11129 * @class Roo.data.DataReader
11130 * Base class for reading structured data from a data source. This class is intended to be
11131 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11134 Roo.data.DataReader = function(meta, recordType){
11138 this.recordType = recordType instanceof Array ?
11139 Roo.data.Record.create(recordType) : recordType;
11142 Roo.data.DataReader.prototype = {
11144 * Create an empty record
11145 * @param {Object} data (optional) - overlay some values
11146 * @return {Roo.data.Record} record created.
11148 newRow : function(d) {
11150 this.recordType.prototype.fields.each(function(c) {
11152 case 'int' : da[c.name] = 0; break;
11153 case 'date' : da[c.name] = new Date(); break;
11154 case 'float' : da[c.name] = 0.0; break;
11155 case 'boolean' : da[c.name] = false; break;
11156 default : da[c.name] = ""; break;
11160 return new this.recordType(Roo.apply(da, d));
11165 * Ext JS Library 1.1.1
11166 * Copyright(c) 2006-2007, Ext JS, LLC.
11168 * Originally Released Under LGPL - original licence link has changed is not relivant.
11171 * <script type="text/javascript">
11175 * @class Roo.data.DataProxy
11176 * @extends Roo.data.Observable
11177 * This class is an abstract base class for implementations which provide retrieval of
11178 * unformatted data objects.<br>
11180 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11181 * (of the appropriate type which knows how to parse the data object) to provide a block of
11182 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11184 * Custom implementations must implement the load method as described in
11185 * {@link Roo.data.HttpProxy#load}.
11187 Roo.data.DataProxy = function(){
11190 * @event beforeload
11191 * Fires before a network request is made to retrieve a data object.
11192 * @param {Object} This DataProxy object.
11193 * @param {Object} params The params parameter to the load function.
11198 * Fires before the load method's callback is called.
11199 * @param {Object} This DataProxy object.
11200 * @param {Object} o The data object.
11201 * @param {Object} arg The callback argument object passed to the load function.
11205 * @event loadexception
11206 * Fires if an Exception occurs during data retrieval.
11207 * @param {Object} This DataProxy object.
11208 * @param {Object} o The data object.
11209 * @param {Object} arg The callback argument object passed to the load function.
11210 * @param {Object} e The Exception.
11212 loadexception : true
11214 Roo.data.DataProxy.superclass.constructor.call(this);
11217 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11220 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11224 * Ext JS Library 1.1.1
11225 * Copyright(c) 2006-2007, Ext JS, LLC.
11227 * Originally Released Under LGPL - original licence link has changed is not relivant.
11230 * <script type="text/javascript">
11233 * @class Roo.data.MemoryProxy
11234 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11235 * to the Reader when its load method is called.
11237 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11239 Roo.data.MemoryProxy = function(data){
11243 Roo.data.MemoryProxy.superclass.constructor.call(this);
11247 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11250 * Load data from the requested source (in this case an in-memory
11251 * data object passed to the constructor), read the data object into
11252 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11253 * process that block using the passed callback.
11254 * @param {Object} params This parameter is not used by the MemoryProxy class.
11255 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11256 * object into a block of Roo.data.Records.
11257 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11258 * The function must be passed <ul>
11259 * <li>The Record block object</li>
11260 * <li>The "arg" argument from the load function</li>
11261 * <li>A boolean success indicator</li>
11263 * @param {Object} scope The scope in which to call the callback
11264 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11266 load : function(params, reader, callback, scope, arg){
11267 params = params || {};
11270 result = reader.readRecords(this.data);
11272 this.fireEvent("loadexception", this, arg, null, e);
11273 callback.call(scope, null, arg, false);
11276 callback.call(scope, result, arg, true);
11280 update : function(params, records){
11285 * Ext JS Library 1.1.1
11286 * Copyright(c) 2006-2007, Ext JS, LLC.
11288 * Originally Released Under LGPL - original licence link has changed is not relivant.
11291 * <script type="text/javascript">
11294 * @class Roo.data.HttpProxy
11295 * @extends Roo.data.DataProxy
11296 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11297 * configured to reference a certain URL.<br><br>
11299 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11300 * from which the running page was served.<br><br>
11302 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11304 * Be aware that to enable the browser to parse an XML document, the server must set
11305 * the Content-Type header in the HTTP response to "text/xml".
11307 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11308 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11309 * will be used to make the request.
11311 Roo.data.HttpProxy = function(conn){
11312 Roo.data.HttpProxy.superclass.constructor.call(this);
11313 // is conn a conn config or a real conn?
11315 this.useAjax = !conn || !conn.events;
11319 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11320 // thse are take from connection...
11323 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11326 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11327 * extra parameters to each request made by this object. (defaults to undefined)
11330 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11331 * to each request made by this object. (defaults to undefined)
11334 * @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)
11337 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11340 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11346 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11350 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11351 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11352 * a finer-grained basis than the DataProxy events.
11354 getConnection : function(){
11355 return this.useAjax ? Roo.Ajax : this.conn;
11359 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11360 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11361 * process that block using the passed callback.
11362 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11363 * for the request to the remote server.
11364 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11365 * object into a block of Roo.data.Records.
11366 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11367 * The function must be passed <ul>
11368 * <li>The Record block object</li>
11369 * <li>The "arg" argument from the load function</li>
11370 * <li>A boolean success indicator</li>
11372 * @param {Object} scope The scope in which to call the callback
11373 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11375 load : function(params, reader, callback, scope, arg){
11376 if(this.fireEvent("beforeload", this, params) !== false){
11378 params : params || {},
11380 callback : callback,
11385 callback : this.loadResponse,
11389 Roo.applyIf(o, this.conn);
11390 if(this.activeRequest){
11391 Roo.Ajax.abort(this.activeRequest);
11393 this.activeRequest = Roo.Ajax.request(o);
11395 this.conn.request(o);
11398 callback.call(scope||this, null, arg, false);
11403 loadResponse : function(o, success, response){
11404 delete this.activeRequest;
11406 this.fireEvent("loadexception", this, o, response);
11407 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11412 result = o.reader.read(response);
11414 this.fireEvent("loadexception", this, o, response, e);
11415 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11419 this.fireEvent("load", this, o, o.request.arg);
11420 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11424 update : function(dataSet){
11429 updateResponse : function(dataSet){
11434 * Ext JS Library 1.1.1
11435 * Copyright(c) 2006-2007, Ext JS, LLC.
11437 * Originally Released Under LGPL - original licence link has changed is not relivant.
11440 * <script type="text/javascript">
11444 * @class Roo.data.ScriptTagProxy
11445 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11446 * other than the originating domain of the running page.<br><br>
11448 * <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
11449 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11451 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11452 * source code that is used as the source inside a <script> tag.<br><br>
11454 * In order for the browser to process the returned data, the server must wrap the data object
11455 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11456 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11457 * depending on whether the callback name was passed:
11460 boolean scriptTag = false;
11461 String cb = request.getParameter("callback");
11464 response.setContentType("text/javascript");
11466 response.setContentType("application/x-json");
11468 Writer out = response.getWriter();
11470 out.write(cb + "(");
11472 out.print(dataBlock.toJsonString());
11479 * @param {Object} config A configuration object.
11481 Roo.data.ScriptTagProxy = function(config){
11482 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11483 Roo.apply(this, config);
11484 this.head = document.getElementsByTagName("head")[0];
11487 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11489 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11491 * @cfg {String} url The URL from which to request the data object.
11494 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11498 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11499 * the server the name of the callback function set up by the load call to process the returned data object.
11500 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11501 * javascript output which calls this named function passing the data object as its only parameter.
11503 callbackParam : "callback",
11505 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11506 * name to the request.
11511 * Load data from the configured URL, read the data object into
11512 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11513 * process that block using the passed callback.
11514 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11515 * for the request to the remote server.
11516 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11517 * object into a block of Roo.data.Records.
11518 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11519 * The function must be passed <ul>
11520 * <li>The Record block object</li>
11521 * <li>The "arg" argument from the load function</li>
11522 * <li>A boolean success indicator</li>
11524 * @param {Object} scope The scope in which to call the callback
11525 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11527 load : function(params, reader, callback, scope, arg){
11528 if(this.fireEvent("beforeload", this, params) !== false){
11530 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11532 var url = this.url;
11533 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11535 url += "&_dc=" + (new Date().getTime());
11537 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11540 cb : "stcCallback"+transId,
11541 scriptId : "stcScript"+transId,
11545 callback : callback,
11551 window[trans.cb] = function(o){
11552 conn.handleResponse(o, trans);
11555 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11557 if(this.autoAbort !== false){
11561 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11563 var script = document.createElement("script");
11564 script.setAttribute("src", url);
11565 script.setAttribute("type", "text/javascript");
11566 script.setAttribute("id", trans.scriptId);
11567 this.head.appendChild(script);
11569 this.trans = trans;
11571 callback.call(scope||this, null, arg, false);
11576 isLoading : function(){
11577 return this.trans ? true : false;
11581 * Abort the current server request.
11583 abort : function(){
11584 if(this.isLoading()){
11585 this.destroyTrans(this.trans);
11590 destroyTrans : function(trans, isLoaded){
11591 this.head.removeChild(document.getElementById(trans.scriptId));
11592 clearTimeout(trans.timeoutId);
11594 window[trans.cb] = undefined;
11596 delete window[trans.cb];
11599 // if hasn't been loaded, wait for load to remove it to prevent script error
11600 window[trans.cb] = function(){
11601 window[trans.cb] = undefined;
11603 delete window[trans.cb];
11610 handleResponse : function(o, trans){
11611 this.trans = false;
11612 this.destroyTrans(trans, true);
11615 result = trans.reader.readRecords(o);
11617 this.fireEvent("loadexception", this, o, trans.arg, e);
11618 trans.callback.call(trans.scope||window, null, trans.arg, false);
11621 this.fireEvent("load", this, o, trans.arg);
11622 trans.callback.call(trans.scope||window, result, trans.arg, true);
11626 handleFailure : function(trans){
11627 this.trans = false;
11628 this.destroyTrans(trans, false);
11629 this.fireEvent("loadexception", this, null, trans.arg);
11630 trans.callback.call(trans.scope||window, null, trans.arg, false);
11634 * Ext JS Library 1.1.1
11635 * Copyright(c) 2006-2007, Ext JS, LLC.
11637 * Originally Released Under LGPL - original licence link has changed is not relivant.
11640 * <script type="text/javascript">
11644 * @class Roo.data.JsonReader
11645 * @extends Roo.data.DataReader
11646 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11647 * based on mappings in a provided Roo.data.Record constructor.
11649 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11650 * in the reply previously.
11655 var RecordDef = Roo.data.Record.create([
11656 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11657 {name: 'occupation'} // This field will use "occupation" as the mapping.
11659 var myReader = new Roo.data.JsonReader({
11660 totalProperty: "results", // The property which contains the total dataset size (optional)
11661 root: "rows", // The property which contains an Array of row objects
11662 id: "id" // The property within each row object that provides an ID for the record (optional)
11666 * This would consume a JSON file like this:
11668 { 'results': 2, 'rows': [
11669 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11670 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11673 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11674 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11675 * paged from the remote server.
11676 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11677 * @cfg {String} root name of the property which contains the Array of row objects.
11678 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11679 * @cfg {Array} fields Array of field definition objects
11681 * Create a new JsonReader
11682 * @param {Object} meta Metadata configuration options
11683 * @param {Object} recordType Either an Array of field definition objects,
11684 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11686 Roo.data.JsonReader = function(meta, recordType){
11689 // set some defaults:
11690 Roo.applyIf(meta, {
11691 totalProperty: 'total',
11692 successProperty : 'success',
11697 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11699 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11702 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11703 * Used by Store query builder to append _requestMeta to params.
11706 metaFromRemote : false,
11708 * This method is only used by a DataProxy which has retrieved data from a remote server.
11709 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11710 * @return {Object} data A data block which is used by an Roo.data.Store object as
11711 * a cache of Roo.data.Records.
11713 read : function(response){
11714 var json = response.responseText;
11716 var o = /* eval:var:o */ eval("("+json+")");
11718 throw {message: "JsonReader.read: Json object not found"};
11724 this.metaFromRemote = true;
11725 this.meta = o.metaData;
11726 this.recordType = Roo.data.Record.create(o.metaData.fields);
11727 this.onMetaChange(this.meta, this.recordType, o);
11729 return this.readRecords(o);
11732 // private function a store will implement
11733 onMetaChange : function(meta, recordType, o){
11740 simpleAccess: function(obj, subsc) {
11747 getJsonAccessor: function(){
11749 return function(expr) {
11751 return(re.test(expr))
11752 ? new Function("obj", "return obj." + expr)
11757 return Roo.emptyFn;
11762 * Create a data block containing Roo.data.Records from an XML document.
11763 * @param {Object} o An object which contains an Array of row objects in the property specified
11764 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11765 * which contains the total size of the dataset.
11766 * @return {Object} data A data block which is used by an Roo.data.Store object as
11767 * a cache of Roo.data.Records.
11769 readRecords : function(o){
11771 * After any data loads, the raw JSON data is available for further custom processing.
11775 var s = this.meta, Record = this.recordType,
11776 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11778 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11780 if(s.totalProperty) {
11781 this.getTotal = this.getJsonAccessor(s.totalProperty);
11783 if(s.successProperty) {
11784 this.getSuccess = this.getJsonAccessor(s.successProperty);
11786 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11788 var g = this.getJsonAccessor(s.id);
11789 this.getId = function(rec) {
11791 return (r === undefined || r === "") ? null : r;
11794 this.getId = function(){return null;};
11797 for(var jj = 0; jj < fl; jj++){
11799 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11800 this.ef[jj] = this.getJsonAccessor(map);
11804 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11805 if(s.totalProperty){
11806 var vt = parseInt(this.getTotal(o), 10);
11811 if(s.successProperty){
11812 var vs = this.getSuccess(o);
11813 if(vs === false || vs === 'false'){
11818 for(var i = 0; i < c; i++){
11821 var id = this.getId(n);
11822 for(var j = 0; j < fl; j++){
11824 var v = this.ef[j](n);
11826 Roo.log('missing convert for ' + f.name);
11830 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11832 var record = new Record(values, id);
11834 records[i] = record;
11840 totalRecords : totalRecords
11845 * Ext JS Library 1.1.1
11846 * Copyright(c) 2006-2007, Ext JS, LLC.
11848 * Originally Released Under LGPL - original licence link has changed is not relivant.
11851 * <script type="text/javascript">
11855 * @class Roo.data.ArrayReader
11856 * @extends Roo.data.DataReader
11857 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11858 * Each element of that Array represents a row of data fields. The
11859 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11860 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11864 var RecordDef = Roo.data.Record.create([
11865 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11866 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11868 var myReader = new Roo.data.ArrayReader({
11869 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11873 * This would consume an Array like this:
11875 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11877 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11879 * Create a new JsonReader
11880 * @param {Object} meta Metadata configuration options.
11881 * @param {Object} recordType Either an Array of field definition objects
11882 * as specified to {@link Roo.data.Record#create},
11883 * or an {@link Roo.data.Record} object
11884 * created using {@link Roo.data.Record#create}.
11886 Roo.data.ArrayReader = function(meta, recordType){
11887 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11890 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11892 * Create a data block containing Roo.data.Records from an XML document.
11893 * @param {Object} o An Array of row objects which represents the dataset.
11894 * @return {Object} data A data block which is used by an Roo.data.Store object as
11895 * a cache of Roo.data.Records.
11897 readRecords : function(o){
11898 var sid = this.meta ? this.meta.id : null;
11899 var recordType = this.recordType, fields = recordType.prototype.fields;
11902 for(var i = 0; i < root.length; i++){
11905 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11906 for(var j = 0, jlen = fields.length; j < jlen; j++){
11907 var f = fields.items[j];
11908 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11909 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11911 values[f.name] = v;
11913 var record = new recordType(values, id);
11915 records[records.length] = record;
11919 totalRecords : records.length
11928 * @class Roo.bootstrap.ComboBox
11929 * @extends Roo.bootstrap.TriggerField
11930 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11931 * @cfg {Boolean} append (true|false) default false
11932 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11933 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11934 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11935 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11936 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11937 * @cfg {Boolean} animate default true
11938 * @cfg {Boolean} emptyResultText only for touch device
11939 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11941 * Create a new ComboBox.
11942 * @param {Object} config Configuration options
11944 Roo.bootstrap.ComboBox = function(config){
11945 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11949 * Fires when the dropdown list is expanded
11950 * @param {Roo.bootstrap.ComboBox} combo This combo box
11955 * Fires when the dropdown list is collapsed
11956 * @param {Roo.bootstrap.ComboBox} combo This combo box
11960 * @event beforeselect
11961 * Fires before a list item is selected. Return false to cancel the selection.
11962 * @param {Roo.bootstrap.ComboBox} combo This combo box
11963 * @param {Roo.data.Record} record The data record returned from the underlying store
11964 * @param {Number} index The index of the selected item in the dropdown list
11966 'beforeselect' : true,
11969 * Fires when a list item is selected
11970 * @param {Roo.bootstrap.ComboBox} combo This combo box
11971 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11972 * @param {Number} index The index of the selected item in the dropdown list
11976 * @event beforequery
11977 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11978 * The event object passed has these properties:
11979 * @param {Roo.bootstrap.ComboBox} combo This combo box
11980 * @param {String} query The query
11981 * @param {Boolean} forceAll true to force "all" query
11982 * @param {Boolean} cancel true to cancel the query
11983 * @param {Object} e The query event object
11985 'beforequery': true,
11988 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11989 * @param {Roo.bootstrap.ComboBox} combo This combo box
11994 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11995 * @param {Roo.bootstrap.ComboBox} combo This combo box
11996 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12001 * Fires when the remove value from the combobox array
12002 * @param {Roo.bootstrap.ComboBox} combo This combo box
12006 * @event afterremove
12007 * Fires when the remove value from the combobox array
12008 * @param {Roo.bootstrap.ComboBox} combo This combo box
12010 'afterremove' : true,
12012 * @event specialfilter
12013 * Fires when specialfilter
12014 * @param {Roo.bootstrap.ComboBox} combo This combo box
12016 'specialfilter' : true,
12019 * Fires when tick the element
12020 * @param {Roo.bootstrap.ComboBox} combo This combo box
12024 * @event touchviewdisplay
12025 * Fires when touch view require special display (default is using displayField)
12026 * @param {Roo.bootstrap.ComboBox} combo This combo box
12027 * @param {Object} cfg set html .
12029 'touchviewdisplay' : true
12034 this.tickItems = [];
12036 this.selectedIndex = -1;
12037 if(this.mode == 'local'){
12038 if(config.queryDelay === undefined){
12039 this.queryDelay = 10;
12041 if(config.minChars === undefined){
12047 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12050 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12051 * rendering into an Roo.Editor, defaults to false)
12054 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12055 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12058 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12061 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12062 * the dropdown list (defaults to undefined, with no header element)
12066 * @cfg {String/Roo.Template} tpl The template to use to render the output
12070 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12072 listWidth: undefined,
12074 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12075 * mode = 'remote' or 'text' if mode = 'local')
12077 displayField: undefined,
12080 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12081 * mode = 'remote' or 'value' if mode = 'local').
12082 * Note: use of a valueField requires the user make a selection
12083 * in order for a value to be mapped.
12085 valueField: undefined,
12087 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12092 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12093 * field's data value (defaults to the underlying DOM element's name)
12095 hiddenName: undefined,
12097 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12101 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12103 selectedClass: 'active',
12106 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12110 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12111 * anchor positions (defaults to 'tl-bl')
12113 listAlign: 'tl-bl?',
12115 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12119 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12120 * query specified by the allQuery config option (defaults to 'query')
12122 triggerAction: 'query',
12124 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12125 * (defaults to 4, does not apply if editable = false)
12129 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12130 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12134 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12135 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12139 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12140 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12144 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12145 * when editable = true (defaults to false)
12147 selectOnFocus:false,
12149 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12151 queryParam: 'query',
12153 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12154 * when mode = 'remote' (defaults to 'Loading...')
12156 loadingText: 'Loading...',
12158 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12162 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12166 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12167 * traditional select (defaults to true)
12171 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12175 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12179 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12180 * listWidth has a higher value)
12184 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12185 * allow the user to set arbitrary text into the field (defaults to false)
12187 forceSelection:false,
12189 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12190 * if typeAhead = true (defaults to 250)
12192 typeAheadDelay : 250,
12194 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12195 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12197 valueNotFoundText : undefined,
12199 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12201 blockFocus : false,
12204 * @cfg {Boolean} disableClear Disable showing of clear button.
12206 disableClear : false,
12208 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12210 alwaysQuery : false,
12213 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12218 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12220 invalidClass : "has-warning",
12223 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12225 validClass : "has-success",
12228 * @cfg {Boolean} specialFilter (true|false) special filter default false
12230 specialFilter : false,
12233 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12235 mobileTouchView : true,
12238 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12240 useNativeIOS : false,
12242 ios_options : false,
12254 btnPosition : 'right',
12255 triggerList : true,
12256 showToggleBtn : true,
12258 emptyResultText: 'Empty',
12259 triggerText : 'Select',
12261 // element that contains real text value.. (when hidden is used..)
12263 getAutoCreate : function()
12268 * Render classic select for iso
12271 if(Roo.isIOS && this.useNativeIOS){
12272 cfg = this.getAutoCreateNativeIOS();
12280 if(Roo.isTouch && this.mobileTouchView){
12281 cfg = this.getAutoCreateTouchView();
12288 if(!this.tickable){
12289 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12294 * ComboBox with tickable selections
12297 var align = this.labelAlign || this.parentLabelAlign();
12300 cls : 'form-group roo-combobox-tickable' //input-group
12305 cls : 'tickable-buttons',
12310 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12311 html : this.triggerText
12317 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12324 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12331 buttons.cn.unshift({
12333 cls: 'roo-select2-search-field-input'
12339 Roo.each(buttons.cn, function(c){
12341 c.cls += ' btn-' + _this.size;
12344 if (_this.disabled) {
12355 cls: 'form-hidden-field'
12359 cls: 'roo-select2-choices',
12363 cls: 'roo-select2-search-field',
12375 cls: 'roo-select2-container input-group roo-select2-container-multi',
12380 // cls: 'typeahead typeahead-long dropdown-menu',
12381 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12386 if(this.hasFeedback && !this.allowBlank){
12390 cls: 'glyphicon form-control-feedback'
12393 combobox.cn.push(feedback);
12396 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12398 // Roo.log("left and has label");
12402 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12403 tooltip : 'This field is required'
12408 cls : 'control-label col-sm-' + this.labelWidth,
12409 html : this.fieldLabel
12413 cls : "col-sm-" + (12 - this.labelWidth),
12421 if(this.indicatorpos == 'right'){
12427 cls : 'control-label col-sm-' + this.labelWidth,
12428 html : this.fieldLabel
12433 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12434 tooltip : 'This field is required'
12437 cls : "col-sm-" + (12 - this.labelWidth),
12448 } else if ( this.fieldLabel.length) {
12449 // Roo.log(" label");
12453 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12454 tooltip : 'This field is required'
12458 //cls : 'input-group-addon',
12459 html : this.fieldLabel
12467 if(this.indicatorpos == 'right'){
12472 //cls : 'input-group-addon',
12473 html : this.fieldLabel
12479 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12480 tooltip : 'This field is required'
12491 // Roo.log(" no label && no align");
12498 ['xs','sm','md','lg'].map(function(size){
12499 if (settings[size]) {
12500 cfg.cls += ' col-' + size + '-' + settings[size];
12508 _initEventsCalled : false,
12511 initEvents: function()
12513 if (this._initEventsCalled) { // as we call render... prevent looping...
12516 this._initEventsCalled = true;
12519 throw "can not find store for combo";
12522 this.store = Roo.factory(this.store, Roo.data);
12524 // if we are building from html. then this element is so complex, that we can not really
12525 // use the rendered HTML.
12526 // so we have to trash and replace the previous code.
12527 if (Roo.XComponent.build_from_html) {
12529 // remove this element....
12530 var e = this.el.dom, k=0;
12531 while (e ) { e = e.previousSibling; ++k;}
12536 this.rendered = false;
12538 this.render(this.parent().getChildContainer(true), k);
12544 if(Roo.isIOS && this.useNativeIOS){
12545 this.initIOSView();
12553 if(Roo.isTouch && this.mobileTouchView){
12554 this.initTouchView();
12559 this.initTickableEvents();
12563 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12565 if(this.hiddenName){
12567 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12569 this.hiddenField.dom.value =
12570 this.hiddenValue !== undefined ? this.hiddenValue :
12571 this.value !== undefined ? this.value : '';
12573 // prevent input submission
12574 this.el.dom.removeAttribute('name');
12575 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12580 // this.el.dom.setAttribute('autocomplete', 'off');
12583 var cls = 'x-combo-list';
12585 //this.list = new Roo.Layer({
12586 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12592 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12593 _this.list.setWidth(lw);
12596 this.list.on('mouseover', this.onViewOver, this);
12597 this.list.on('mousemove', this.onViewMove, this);
12599 this.list.on('scroll', this.onViewScroll, this);
12602 this.list.swallowEvent('mousewheel');
12603 this.assetHeight = 0;
12606 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12607 this.assetHeight += this.header.getHeight();
12610 this.innerList = this.list.createChild({cls:cls+'-inner'});
12611 this.innerList.on('mouseover', this.onViewOver, this);
12612 this.innerList.on('mousemove', this.onViewMove, this);
12613 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12615 if(this.allowBlank && !this.pageSize && !this.disableClear){
12616 this.footer = this.list.createChild({cls:cls+'-ft'});
12617 this.pageTb = new Roo.Toolbar(this.footer);
12621 this.footer = this.list.createChild({cls:cls+'-ft'});
12622 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12623 {pageSize: this.pageSize});
12627 if (this.pageTb && this.allowBlank && !this.disableClear) {
12629 this.pageTb.add(new Roo.Toolbar.Fill(), {
12630 cls: 'x-btn-icon x-btn-clear',
12632 handler: function()
12635 _this.clearValue();
12636 _this.onSelect(false, -1);
12641 this.assetHeight += this.footer.getHeight();
12646 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12649 this.view = new Roo.View(this.list, this.tpl, {
12650 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12652 //this.view.wrapEl.setDisplayed(false);
12653 this.view.on('click', this.onViewClick, this);
12657 this.store.on('beforeload', this.onBeforeLoad, this);
12658 this.store.on('load', this.onLoad, this);
12659 this.store.on('loadexception', this.onLoadException, this);
12661 if(this.resizable){
12662 this.resizer = new Roo.Resizable(this.list, {
12663 pinned:true, handles:'se'
12665 this.resizer.on('resize', function(r, w, h){
12666 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12667 this.listWidth = w;
12668 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12669 this.restrictHeight();
12671 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12674 if(!this.editable){
12675 this.editable = true;
12676 this.setEditable(false);
12681 if (typeof(this.events.add.listeners) != 'undefined') {
12683 this.addicon = this.wrap.createChild(
12684 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12686 this.addicon.on('click', function(e) {
12687 this.fireEvent('add', this);
12690 if (typeof(this.events.edit.listeners) != 'undefined') {
12692 this.editicon = this.wrap.createChild(
12693 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12694 if (this.addicon) {
12695 this.editicon.setStyle('margin-left', '40px');
12697 this.editicon.on('click', function(e) {
12699 // we fire even if inothing is selected..
12700 this.fireEvent('edit', this, this.lastData );
12706 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12707 "up" : function(e){
12708 this.inKeyMode = true;
12712 "down" : function(e){
12713 if(!this.isExpanded()){
12714 this.onTriggerClick();
12716 this.inKeyMode = true;
12721 "enter" : function(e){
12722 // this.onViewClick();
12726 if(this.fireEvent("specialkey", this, e)){
12727 this.onViewClick(false);
12733 "esc" : function(e){
12737 "tab" : function(e){
12740 if(this.fireEvent("specialkey", this, e)){
12741 this.onViewClick(false);
12749 doRelay : function(foo, bar, hname){
12750 if(hname == 'down' || this.scope.isExpanded()){
12751 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12760 this.queryDelay = Math.max(this.queryDelay || 10,
12761 this.mode == 'local' ? 10 : 250);
12764 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12766 if(this.typeAhead){
12767 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12769 if(this.editable !== false){
12770 this.inputEl().on("keyup", this.onKeyUp, this);
12772 if(this.forceSelection){
12773 this.inputEl().on('blur', this.doForce, this);
12777 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12778 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12782 initTickableEvents: function()
12786 if(this.hiddenName){
12788 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12790 this.hiddenField.dom.value =
12791 this.hiddenValue !== undefined ? this.hiddenValue :
12792 this.value !== undefined ? this.value : '';
12794 // prevent input submission
12795 this.el.dom.removeAttribute('name');
12796 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12801 // this.list = this.el.select('ul.dropdown-menu',true).first();
12803 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12804 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12805 if(this.triggerList){
12806 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12809 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12810 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12812 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12813 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12815 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12816 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12818 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12819 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12820 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12823 this.cancelBtn.hide();
12828 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12829 _this.list.setWidth(lw);
12832 this.list.on('mouseover', this.onViewOver, this);
12833 this.list.on('mousemove', this.onViewMove, this);
12835 this.list.on('scroll', this.onViewScroll, this);
12838 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>';
12841 this.view = new Roo.View(this.list, this.tpl, {
12842 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12845 //this.view.wrapEl.setDisplayed(false);
12846 this.view.on('click', this.onViewClick, this);
12850 this.store.on('beforeload', this.onBeforeLoad, this);
12851 this.store.on('load', this.onLoad, this);
12852 this.store.on('loadexception', this.onLoadException, this);
12855 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12856 "up" : function(e){
12857 this.inKeyMode = true;
12861 "down" : function(e){
12862 this.inKeyMode = true;
12866 "enter" : function(e){
12867 if(this.fireEvent("specialkey", this, e)){
12868 this.onViewClick(false);
12874 "esc" : function(e){
12875 this.onTickableFooterButtonClick(e, false, false);
12878 "tab" : function(e){
12879 this.fireEvent("specialkey", this, e);
12881 this.onTickableFooterButtonClick(e, false, false);
12888 doRelay : function(e, fn, key){
12889 if(this.scope.isExpanded()){
12890 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12899 this.queryDelay = Math.max(this.queryDelay || 10,
12900 this.mode == 'local' ? 10 : 250);
12903 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12905 if(this.typeAhead){
12906 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12909 if(this.editable !== false){
12910 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12915 onDestroy : function(){
12917 this.view.setStore(null);
12918 this.view.el.removeAllListeners();
12919 this.view.el.remove();
12920 this.view.purgeListeners();
12923 this.list.dom.innerHTML = '';
12927 this.store.un('beforeload', this.onBeforeLoad, this);
12928 this.store.un('load', this.onLoad, this);
12929 this.store.un('loadexception', this.onLoadException, this);
12931 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12935 fireKey : function(e){
12936 if(e.isNavKeyPress() && !this.list.isVisible()){
12937 this.fireEvent("specialkey", this, e);
12942 onResize: function(w, h){
12943 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12945 // if(typeof w != 'number'){
12946 // // we do not handle it!?!?
12949 // var tw = this.trigger.getWidth();
12950 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12951 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12953 // this.inputEl().setWidth( this.adjustWidth('input', x));
12955 // //this.trigger.setStyle('left', x+'px');
12957 // if(this.list && this.listWidth === undefined){
12958 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12959 // this.list.setWidth(lw);
12960 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12968 * Allow or prevent the user from directly editing the field text. If false is passed,
12969 * the user will only be able to select from the items defined in the dropdown list. This method
12970 * is the runtime equivalent of setting the 'editable' config option at config time.
12971 * @param {Boolean} value True to allow the user to directly edit the field text
12973 setEditable : function(value){
12974 if(value == this.editable){
12977 this.editable = value;
12979 this.inputEl().dom.setAttribute('readOnly', true);
12980 this.inputEl().on('mousedown', this.onTriggerClick, this);
12981 this.inputEl().addClass('x-combo-noedit');
12983 this.inputEl().dom.setAttribute('readOnly', false);
12984 this.inputEl().un('mousedown', this.onTriggerClick, this);
12985 this.inputEl().removeClass('x-combo-noedit');
12991 onBeforeLoad : function(combo,opts){
12992 if(!this.hasFocus){
12996 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12998 this.restrictHeight();
12999 this.selectedIndex = -1;
13003 onLoad : function(){
13005 this.hasQuery = false;
13007 if(!this.hasFocus){
13011 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13012 this.loading.hide();
13015 if(this.store.getCount() > 0){
13017 this.restrictHeight();
13018 if(this.lastQuery == this.allQuery){
13019 if(this.editable && !this.tickable){
13020 this.inputEl().dom.select();
13024 !this.selectByValue(this.value, true) &&
13027 !this.store.lastOptions ||
13028 typeof(this.store.lastOptions.add) == 'undefined' ||
13029 this.store.lastOptions.add != true
13032 this.select(0, true);
13035 if(this.autoFocus){
13038 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13039 this.taTask.delay(this.typeAheadDelay);
13043 this.onEmptyResults();
13049 onLoadException : function()
13051 this.hasQuery = false;
13053 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13054 this.loading.hide();
13057 if(this.tickable && this.editable){
13062 // only causes errors at present
13063 //Roo.log(this.store.reader.jsonData);
13064 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13066 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13072 onTypeAhead : function(){
13073 if(this.store.getCount() > 0){
13074 var r = this.store.getAt(0);
13075 var newValue = r.data[this.displayField];
13076 var len = newValue.length;
13077 var selStart = this.getRawValue().length;
13079 if(selStart != len){
13080 this.setRawValue(newValue);
13081 this.selectText(selStart, newValue.length);
13087 onSelect : function(record, index){
13089 if(this.fireEvent('beforeselect', this, record, index) !== false){
13091 this.setFromData(index > -1 ? record.data : false);
13094 this.fireEvent('select', this, record, index);
13099 * Returns the currently selected field value or empty string if no value is set.
13100 * @return {String} value The selected value
13102 getValue : function()
13104 if(Roo.isIOS && this.useNativeIOS){
13105 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13109 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13112 if(this.valueField){
13113 return typeof this.value != 'undefined' ? this.value : '';
13115 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13119 getRawValue : function()
13121 if(Roo.isIOS && this.useNativeIOS){
13122 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13125 var v = this.inputEl().getValue();
13131 * Clears any text/value currently set in the field
13133 clearValue : function(){
13135 if(this.hiddenField){
13136 this.hiddenField.dom.value = '';
13139 this.setRawValue('');
13140 this.lastSelectionText = '';
13141 this.lastData = false;
13143 var close = this.closeTriggerEl();
13154 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13155 * will be displayed in the field. If the value does not match the data value of an existing item,
13156 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13157 * Otherwise the field will be blank (although the value will still be set).
13158 * @param {String} value The value to match
13160 setValue : function(v)
13162 if(Roo.isIOS && this.useNativeIOS){
13163 this.setIOSValue(v);
13173 if(this.valueField){
13174 var r = this.findRecord(this.valueField, v);
13176 text = r.data[this.displayField];
13177 }else if(this.valueNotFoundText !== undefined){
13178 text = this.valueNotFoundText;
13181 this.lastSelectionText = text;
13182 if(this.hiddenField){
13183 this.hiddenField.dom.value = v;
13185 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13188 var close = this.closeTriggerEl();
13191 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13197 * @property {Object} the last set data for the element
13202 * Sets the value of the field based on a object which is related to the record format for the store.
13203 * @param {Object} value the value to set as. or false on reset?
13205 setFromData : function(o){
13212 var dv = ''; // display value
13213 var vv = ''; // value value..
13215 if (this.displayField) {
13216 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13218 // this is an error condition!!!
13219 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13222 if(this.valueField){
13223 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13226 var close = this.closeTriggerEl();
13229 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13232 if(this.hiddenField){
13233 this.hiddenField.dom.value = vv;
13235 this.lastSelectionText = dv;
13236 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13240 // no hidden field.. - we store the value in 'value', but still display
13241 // display field!!!!
13242 this.lastSelectionText = dv;
13243 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13250 reset : function(){
13251 // overridden so that last data is reset..
13258 this.setValue(this.originalValue);
13259 //this.clearInvalid();
13260 this.lastData = false;
13262 this.view.clearSelections();
13268 findRecord : function(prop, value){
13270 if(this.store.getCount() > 0){
13271 this.store.each(function(r){
13272 if(r.data[prop] == value){
13282 getName: function()
13284 // returns hidden if it's set..
13285 if (!this.rendered) {return ''};
13286 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13290 onViewMove : function(e, t){
13291 this.inKeyMode = false;
13295 onViewOver : function(e, t){
13296 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13299 var item = this.view.findItemFromChild(t);
13302 var index = this.view.indexOf(item);
13303 this.select(index, false);
13308 onViewClick : function(view, doFocus, el, e)
13310 var index = this.view.getSelectedIndexes()[0];
13312 var r = this.store.getAt(index);
13316 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13323 Roo.each(this.tickItems, function(v,k){
13325 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13327 _this.tickItems.splice(k, 1);
13329 if(typeof(e) == 'undefined' && view == false){
13330 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13342 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13343 this.tickItems.push(r.data);
13346 if(typeof(e) == 'undefined' && view == false){
13347 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13354 this.onSelect(r, index);
13356 if(doFocus !== false && !this.blockFocus){
13357 this.inputEl().focus();
13362 restrictHeight : function(){
13363 //this.innerList.dom.style.height = '';
13364 //var inner = this.innerList.dom;
13365 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13366 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13367 //this.list.beginUpdate();
13368 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13369 this.list.alignTo(this.inputEl(), this.listAlign);
13370 this.list.alignTo(this.inputEl(), this.listAlign);
13371 //this.list.endUpdate();
13375 onEmptyResults : function(){
13377 if(this.tickable && this.editable){
13378 this.restrictHeight();
13386 * Returns true if the dropdown list is expanded, else false.
13388 isExpanded : function(){
13389 return this.list.isVisible();
13393 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13394 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13395 * @param {String} value The data value of the item to select
13396 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13397 * selected item if it is not currently in view (defaults to true)
13398 * @return {Boolean} True if the value matched an item in the list, else false
13400 selectByValue : function(v, scrollIntoView){
13401 if(v !== undefined && v !== null){
13402 var r = this.findRecord(this.valueField || this.displayField, v);
13404 this.select(this.store.indexOf(r), scrollIntoView);
13412 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13413 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13414 * @param {Number} index The zero-based index of the list item to select
13415 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13416 * selected item if it is not currently in view (defaults to true)
13418 select : function(index, scrollIntoView){
13419 this.selectedIndex = index;
13420 this.view.select(index);
13421 if(scrollIntoView !== false){
13422 var el = this.view.getNode(index);
13424 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13427 this.list.scrollChildIntoView(el, false);
13433 selectNext : function(){
13434 var ct = this.store.getCount();
13436 if(this.selectedIndex == -1){
13438 }else if(this.selectedIndex < ct-1){
13439 this.select(this.selectedIndex+1);
13445 selectPrev : function(){
13446 var ct = this.store.getCount();
13448 if(this.selectedIndex == -1){
13450 }else if(this.selectedIndex != 0){
13451 this.select(this.selectedIndex-1);
13457 onKeyUp : function(e){
13458 if(this.editable !== false && !e.isSpecialKey()){
13459 this.lastKey = e.getKey();
13460 this.dqTask.delay(this.queryDelay);
13465 validateBlur : function(){
13466 return !this.list || !this.list.isVisible();
13470 initQuery : function(){
13472 var v = this.getRawValue();
13474 if(this.tickable && this.editable){
13475 v = this.tickableInputEl().getValue();
13482 doForce : function(){
13483 if(this.inputEl().dom.value.length > 0){
13484 this.inputEl().dom.value =
13485 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13491 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13492 * query allowing the query action to be canceled if needed.
13493 * @param {String} query The SQL query to execute
13494 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13495 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13496 * saved in the current store (defaults to false)
13498 doQuery : function(q, forceAll){
13500 if(q === undefined || q === null){
13505 forceAll: forceAll,
13509 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13514 forceAll = qe.forceAll;
13515 if(forceAll === true || (q.length >= this.minChars)){
13517 this.hasQuery = true;
13519 if(this.lastQuery != q || this.alwaysQuery){
13520 this.lastQuery = q;
13521 if(this.mode == 'local'){
13522 this.selectedIndex = -1;
13524 this.store.clearFilter();
13527 if(this.specialFilter){
13528 this.fireEvent('specialfilter', this);
13533 this.store.filter(this.displayField, q);
13536 this.store.fireEvent("datachanged", this.store);
13543 this.store.baseParams[this.queryParam] = q;
13545 var options = {params : this.getParams(q)};
13548 options.add = true;
13549 options.params.start = this.page * this.pageSize;
13552 this.store.load(options);
13555 * this code will make the page width larger, at the beginning, the list not align correctly,
13556 * we should expand the list on onLoad
13557 * so command out it
13562 this.selectedIndex = -1;
13567 this.loadNext = false;
13571 getParams : function(q){
13573 //p[this.queryParam] = q;
13577 p.limit = this.pageSize;
13583 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13585 collapse : function(){
13586 if(!this.isExpanded()){
13593 this.hasFocus = false;
13595 this.cancelBtn.hide();
13596 this.trigger.show();
13599 this.tickableInputEl().dom.value = '';
13600 this.tickableInputEl().blur();
13605 Roo.get(document).un('mousedown', this.collapseIf, this);
13606 Roo.get(document).un('mousewheel', this.collapseIf, this);
13607 if (!this.editable) {
13608 Roo.get(document).un('keydown', this.listKeyPress, this);
13610 this.fireEvent('collapse', this);
13616 collapseIf : function(e){
13617 var in_combo = e.within(this.el);
13618 var in_list = e.within(this.list);
13619 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13621 if (in_combo || in_list || is_list) {
13622 //e.stopPropagation();
13627 this.onTickableFooterButtonClick(e, false, false);
13635 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13637 expand : function(){
13639 if(this.isExpanded() || !this.hasFocus){
13643 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13644 this.list.setWidth(lw);
13651 this.restrictHeight();
13655 this.tickItems = Roo.apply([], this.item);
13658 this.cancelBtn.show();
13659 this.trigger.hide();
13662 this.tickableInputEl().focus();
13667 Roo.get(document).on('mousedown', this.collapseIf, this);
13668 Roo.get(document).on('mousewheel', this.collapseIf, this);
13669 if (!this.editable) {
13670 Roo.get(document).on('keydown', this.listKeyPress, this);
13673 this.fireEvent('expand', this);
13677 // Implements the default empty TriggerField.onTriggerClick function
13678 onTriggerClick : function(e)
13680 Roo.log('trigger click');
13682 if(this.disabled || !this.triggerList){
13687 this.loadNext = false;
13689 if(this.isExpanded()){
13691 if (!this.blockFocus) {
13692 this.inputEl().focus();
13696 this.hasFocus = true;
13697 if(this.triggerAction == 'all') {
13698 this.doQuery(this.allQuery, true);
13700 this.doQuery(this.getRawValue());
13702 if (!this.blockFocus) {
13703 this.inputEl().focus();
13708 onTickableTriggerClick : function(e)
13715 this.loadNext = false;
13716 this.hasFocus = true;
13718 if(this.triggerAction == 'all') {
13719 this.doQuery(this.allQuery, true);
13721 this.doQuery(this.getRawValue());
13725 onSearchFieldClick : function(e)
13727 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13728 this.onTickableFooterButtonClick(e, false, false);
13732 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13737 this.loadNext = false;
13738 this.hasFocus = true;
13740 if(this.triggerAction == 'all') {
13741 this.doQuery(this.allQuery, true);
13743 this.doQuery(this.getRawValue());
13747 listKeyPress : function(e)
13749 //Roo.log('listkeypress');
13750 // scroll to first matching element based on key pres..
13751 if (e.isSpecialKey()) {
13754 var k = String.fromCharCode(e.getKey()).toUpperCase();
13757 var csel = this.view.getSelectedNodes();
13758 var cselitem = false;
13760 var ix = this.view.indexOf(csel[0]);
13761 cselitem = this.store.getAt(ix);
13762 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13768 this.store.each(function(v) {
13770 // start at existing selection.
13771 if (cselitem.id == v.id) {
13777 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13778 match = this.store.indexOf(v);
13784 if (match === false) {
13785 return true; // no more action?
13788 this.view.select(match);
13789 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13790 sn.scrollIntoView(sn.dom.parentNode, false);
13793 onViewScroll : function(e, t){
13795 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){
13799 this.hasQuery = true;
13801 this.loading = this.list.select('.loading', true).first();
13803 if(this.loading === null){
13804 this.list.createChild({
13806 cls: 'loading roo-select2-more-results roo-select2-active',
13807 html: 'Loading more results...'
13810 this.loading = this.list.select('.loading', true).first();
13812 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13814 this.loading.hide();
13817 this.loading.show();
13822 this.loadNext = true;
13824 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13829 addItem : function(o)
13831 var dv = ''; // display value
13833 if (this.displayField) {
13834 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13836 // this is an error condition!!!
13837 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13844 var choice = this.choices.createChild({
13846 cls: 'roo-select2-search-choice',
13855 cls: 'roo-select2-search-choice-close',
13860 }, this.searchField);
13862 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13864 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13872 this.inputEl().dom.value = '';
13877 onRemoveItem : function(e, _self, o)
13879 e.preventDefault();
13881 this.lastItem = Roo.apply([], this.item);
13883 var index = this.item.indexOf(o.data) * 1;
13886 Roo.log('not this item?!');
13890 this.item.splice(index, 1);
13895 this.fireEvent('remove', this, e);
13901 syncValue : function()
13903 if(!this.item.length){
13910 Roo.each(this.item, function(i){
13911 if(_this.valueField){
13912 value.push(i[_this.valueField]);
13919 this.value = value.join(',');
13921 if(this.hiddenField){
13922 this.hiddenField.dom.value = this.value;
13925 this.store.fireEvent("datachanged", this.store);
13930 clearItem : function()
13932 if(!this.multiple){
13938 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13946 if(this.tickable && !Roo.isTouch){
13947 this.view.refresh();
13951 inputEl: function ()
13953 if(Roo.isIOS && this.useNativeIOS){
13954 return this.el.select('select.roo-ios-select', true).first();
13957 if(Roo.isTouch && this.mobileTouchView){
13958 return this.el.select('input.form-control',true).first();
13962 return this.searchField;
13965 return this.el.select('input.form-control',true).first();
13968 onTickableFooterButtonClick : function(e, btn, el)
13970 e.preventDefault();
13972 this.lastItem = Roo.apply([], this.item);
13974 if(btn && btn.name == 'cancel'){
13975 this.tickItems = Roo.apply([], this.item);
13984 Roo.each(this.tickItems, function(o){
13992 validate : function()
13994 var v = this.getRawValue();
13997 v = this.getValue();
14000 if(this.disabled || this.allowBlank || v.length){
14005 this.markInvalid();
14009 tickableInputEl : function()
14011 if(!this.tickable || !this.editable){
14012 return this.inputEl();
14015 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14019 getAutoCreateTouchView : function()
14024 cls: 'form-group' //input-group
14030 type : this.inputType,
14031 cls : 'form-control x-combo-noedit',
14032 autocomplete: 'new-password',
14033 placeholder : this.placeholder || '',
14038 input.name = this.name;
14042 input.cls += ' input-' + this.size;
14045 if (this.disabled) {
14046 input.disabled = true;
14057 inputblock.cls += ' input-group';
14059 inputblock.cn.unshift({
14061 cls : 'input-group-addon',
14066 if(this.removable && !this.multiple){
14067 inputblock.cls += ' roo-removable';
14069 inputblock.cn.push({
14072 cls : 'roo-combo-removable-btn close'
14076 if(this.hasFeedback && !this.allowBlank){
14078 inputblock.cls += ' has-feedback';
14080 inputblock.cn.push({
14082 cls: 'glyphicon form-control-feedback'
14089 inputblock.cls += (this.before) ? '' : ' input-group';
14091 inputblock.cn.push({
14093 cls : 'input-group-addon',
14104 cls: 'form-hidden-field'
14118 cls: 'form-hidden-field'
14122 cls: 'roo-select2-choices',
14126 cls: 'roo-select2-search-field',
14139 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14145 if(!this.multiple && this.showToggleBtn){
14152 if (this.caret != false) {
14155 cls: 'fa fa-' + this.caret
14162 cls : 'input-group-addon btn dropdown-toggle',
14167 cls: 'combobox-clear',
14181 combobox.cls += ' roo-select2-container-multi';
14184 var align = this.labelAlign || this.parentLabelAlign();
14188 if(this.fieldLabel.length && this.labelWidth){
14190 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14191 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14196 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14197 tooltip : 'This field is required'
14201 cls : 'control-label ' + lw,
14202 html : this.fieldLabel
14213 if(this.indicatorpos == 'right'){
14217 cls : 'control-label ' + lw,
14218 html : this.fieldLabel
14223 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14224 tooltip : 'This field is required'
14236 var settings = this;
14238 ['xs','sm','md','lg'].map(function(size){
14239 if (settings[size]) {
14240 cfg.cls += ' col-' + size + '-' + settings[size];
14247 initTouchView : function()
14249 this.renderTouchView();
14251 this.touchViewEl.on('scroll', function(){
14252 this.el.dom.scrollTop = 0;
14255 this.originalValue = this.getValue();
14257 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14259 this.inputEl().on("click", this.showTouchView, this);
14260 this.triggerEl.on("click", this.showTouchView, this);
14262 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14263 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14265 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14267 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14268 this.store.on('load', this.onTouchViewLoad, this);
14269 this.store.on('loadexception', this.onTouchViewLoadException, this);
14271 if(this.hiddenName){
14273 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14275 this.hiddenField.dom.value =
14276 this.hiddenValue !== undefined ? this.hiddenValue :
14277 this.value !== undefined ? this.value : '';
14279 this.el.dom.removeAttribute('name');
14280 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14284 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14285 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14288 if(this.removable && !this.multiple){
14289 var close = this.closeTriggerEl();
14291 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14292 close.on('click', this.removeBtnClick, this, close);
14296 * fix the bug in Safari iOS8
14298 this.inputEl().on("focus", function(e){
14299 document.activeElement.blur();
14307 renderTouchView : function()
14309 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14310 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14312 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14313 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14315 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14316 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14317 this.touchViewBodyEl.setStyle('overflow', 'auto');
14319 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14320 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14322 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14323 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14327 showTouchView : function()
14333 this.touchViewHeaderEl.hide();
14335 if(this.modalTitle.length){
14336 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14337 this.touchViewHeaderEl.show();
14340 this.touchViewEl.show();
14342 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14343 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14344 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14346 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14348 if(this.modalTitle.length){
14349 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14352 this.touchViewBodyEl.setHeight(bodyHeight);
14356 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14358 this.touchViewEl.addClass('in');
14361 this.doTouchViewQuery();
14365 hideTouchView : function()
14367 this.touchViewEl.removeClass('in');
14371 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14373 this.touchViewEl.setStyle('display', 'none');
14378 setTouchViewValue : function()
14385 Roo.each(this.tickItems, function(o){
14390 this.hideTouchView();
14393 doTouchViewQuery : function()
14402 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14406 if(!this.alwaysQuery || this.mode == 'local'){
14407 this.onTouchViewLoad();
14414 onTouchViewBeforeLoad : function(combo,opts)
14420 onTouchViewLoad : function()
14422 if(this.store.getCount() < 1){
14423 this.onTouchViewEmptyResults();
14427 this.clearTouchView();
14429 var rawValue = this.getRawValue();
14431 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14433 this.tickItems = [];
14435 this.store.data.each(function(d, rowIndex){
14436 var row = this.touchViewListGroup.createChild(template);
14438 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14439 row.addClass(d.data.cls);
14442 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14445 html : d.data[this.displayField]
14448 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14449 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14452 row.removeClass('selected');
14453 if(!this.multiple && this.valueField &&
14454 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14457 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14458 row.addClass('selected');
14461 if(this.multiple && this.valueField &&
14462 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14466 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14467 this.tickItems.push(d.data);
14470 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14474 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14476 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14478 if(this.modalTitle.length){
14479 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14482 var listHeight = this.touchViewListGroup.getHeight();
14486 if(firstChecked && listHeight > bodyHeight){
14487 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14492 onTouchViewLoadException : function()
14494 this.hideTouchView();
14497 onTouchViewEmptyResults : function()
14499 this.clearTouchView();
14501 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14503 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14507 clearTouchView : function()
14509 this.touchViewListGroup.dom.innerHTML = '';
14512 onTouchViewClick : function(e, el, o)
14514 e.preventDefault();
14517 var rowIndex = o.rowIndex;
14519 var r = this.store.getAt(rowIndex);
14521 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14523 if(!this.multiple){
14524 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14525 c.dom.removeAttribute('checked');
14528 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14530 this.setFromData(r.data);
14532 var close = this.closeTriggerEl();
14538 this.hideTouchView();
14540 this.fireEvent('select', this, r, rowIndex);
14545 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14546 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14547 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14551 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14552 this.addItem(r.data);
14553 this.tickItems.push(r.data);
14557 getAutoCreateNativeIOS : function()
14560 cls: 'form-group' //input-group,
14565 cls : 'roo-ios-select'
14569 combobox.name = this.name;
14572 if (this.disabled) {
14573 combobox.disabled = true;
14576 var settings = this;
14578 ['xs','sm','md','lg'].map(function(size){
14579 if (settings[size]) {
14580 cfg.cls += ' col-' + size + '-' + settings[size];
14590 initIOSView : function()
14592 this.store.on('load', this.onIOSViewLoad, this);
14597 onIOSViewLoad : function()
14599 if(this.store.getCount() < 1){
14603 this.clearIOSView();
14605 if(this.allowBlank) {
14607 var default_text = '-- SELECT --';
14609 var opt = this.inputEl().createChild({
14612 html : default_text
14616 o[this.valueField] = 0;
14617 o[this.displayField] = default_text;
14619 this.ios_options.push({
14626 this.store.data.each(function(d, rowIndex){
14630 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14631 html = d.data[this.displayField];
14636 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14637 value = d.data[this.valueField];
14646 if(this.value == d.data[this.valueField]){
14647 option['selected'] = true;
14650 var opt = this.inputEl().createChild(option);
14652 this.ios_options.push({
14659 this.inputEl().on('change', function(){
14660 this.fireEvent('select', this);
14665 clearIOSView: function()
14667 this.inputEl().dom.innerHTML = '';
14669 this.ios_options = [];
14672 setIOSValue: function(v)
14676 if(!this.ios_options){
14680 Roo.each(this.ios_options, function(opts){
14682 opts.el.dom.removeAttribute('selected');
14684 if(opts.data[this.valueField] != v){
14688 opts.el.dom.setAttribute('selected', true);
14694 * @cfg {Boolean} grow
14698 * @cfg {Number} growMin
14702 * @cfg {Number} growMax
14711 Roo.apply(Roo.bootstrap.ComboBox, {
14715 cls: 'modal-header',
14737 cls: 'list-group-item',
14741 cls: 'roo-combobox-list-group-item-value'
14745 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14759 listItemCheckbox : {
14761 cls: 'list-group-item',
14765 cls: 'roo-combobox-list-group-item-value'
14769 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14785 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14790 cls: 'modal-footer',
14798 cls: 'col-xs-6 text-left',
14801 cls: 'btn btn-danger roo-touch-view-cancel',
14807 cls: 'col-xs-6 text-right',
14810 cls: 'btn btn-success roo-touch-view-ok',
14821 Roo.apply(Roo.bootstrap.ComboBox, {
14823 touchViewTemplate : {
14825 cls: 'modal fade roo-combobox-touch-view',
14829 cls: 'modal-dialog',
14830 style : 'position:fixed', // we have to fix position....
14834 cls: 'modal-content',
14836 Roo.bootstrap.ComboBox.header,
14837 Roo.bootstrap.ComboBox.body,
14838 Roo.bootstrap.ComboBox.footer
14847 * Ext JS Library 1.1.1
14848 * Copyright(c) 2006-2007, Ext JS, LLC.
14850 * Originally Released Under LGPL - original licence link has changed is not relivant.
14853 * <script type="text/javascript">
14858 * @extends Roo.util.Observable
14859 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14860 * This class also supports single and multi selection modes. <br>
14861 * Create a data model bound view:
14863 var store = new Roo.data.Store(...);
14865 var view = new Roo.View({
14867 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14869 singleSelect: true,
14870 selectedClass: "ydataview-selected",
14874 // listen for node click?
14875 view.on("click", function(vw, index, node, e){
14876 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14880 dataModel.load("foobar.xml");
14882 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14884 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14885 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14887 * Note: old style constructor is still suported (container, template, config)
14890 * Create a new View
14891 * @param {Object} config The config object
14894 Roo.View = function(config, depreciated_tpl, depreciated_config){
14896 this.parent = false;
14898 if (typeof(depreciated_tpl) == 'undefined') {
14899 // new way.. - universal constructor.
14900 Roo.apply(this, config);
14901 this.el = Roo.get(this.el);
14904 this.el = Roo.get(config);
14905 this.tpl = depreciated_tpl;
14906 Roo.apply(this, depreciated_config);
14908 this.wrapEl = this.el.wrap().wrap();
14909 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14912 if(typeof(this.tpl) == "string"){
14913 this.tpl = new Roo.Template(this.tpl);
14915 // support xtype ctors..
14916 this.tpl = new Roo.factory(this.tpl, Roo);
14920 this.tpl.compile();
14925 * @event beforeclick
14926 * Fires before a click is processed. Returns false to cancel the default action.
14927 * @param {Roo.View} this
14928 * @param {Number} index The index of the target node
14929 * @param {HTMLElement} node The target node
14930 * @param {Roo.EventObject} e The raw event object
14932 "beforeclick" : true,
14935 * Fires when a template node is clicked.
14936 * @param {Roo.View} this
14937 * @param {Number} index The index of the target node
14938 * @param {HTMLElement} node The target node
14939 * @param {Roo.EventObject} e The raw event object
14944 * Fires when a template node is double clicked.
14945 * @param {Roo.View} this
14946 * @param {Number} index The index of the target node
14947 * @param {HTMLElement} node The target node
14948 * @param {Roo.EventObject} e The raw event object
14952 * @event contextmenu
14953 * Fires when a template node is right clicked.
14954 * @param {Roo.View} this
14955 * @param {Number} index The index of the target node
14956 * @param {HTMLElement} node The target node
14957 * @param {Roo.EventObject} e The raw event object
14959 "contextmenu" : true,
14961 * @event selectionchange
14962 * Fires when the selected nodes change.
14963 * @param {Roo.View} this
14964 * @param {Array} selections Array of the selected nodes
14966 "selectionchange" : true,
14969 * @event beforeselect
14970 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14971 * @param {Roo.View} this
14972 * @param {HTMLElement} node The node to be selected
14973 * @param {Array} selections Array of currently selected nodes
14975 "beforeselect" : true,
14977 * @event preparedata
14978 * Fires on every row to render, to allow you to change the data.
14979 * @param {Roo.View} this
14980 * @param {Object} data to be rendered (change this)
14982 "preparedata" : true
14990 "click": this.onClick,
14991 "dblclick": this.onDblClick,
14992 "contextmenu": this.onContextMenu,
14996 this.selections = [];
14998 this.cmp = new Roo.CompositeElementLite([]);
15000 this.store = Roo.factory(this.store, Roo.data);
15001 this.setStore(this.store, true);
15004 if ( this.footer && this.footer.xtype) {
15006 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15008 this.footer.dataSource = this.store;
15009 this.footer.container = fctr;
15010 this.footer = Roo.factory(this.footer, Roo);
15011 fctr.insertFirst(this.el);
15013 // this is a bit insane - as the paging toolbar seems to detach the el..
15014 // dom.parentNode.parentNode.parentNode
15015 // they get detached?
15019 Roo.View.superclass.constructor.call(this);
15024 Roo.extend(Roo.View, Roo.util.Observable, {
15027 * @cfg {Roo.data.Store} store Data store to load data from.
15032 * @cfg {String|Roo.Element} el The container element.
15037 * @cfg {String|Roo.Template} tpl The template used by this View
15041 * @cfg {String} dataName the named area of the template to use as the data area
15042 * Works with domtemplates roo-name="name"
15046 * @cfg {String} selectedClass The css class to add to selected nodes
15048 selectedClass : "x-view-selected",
15050 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15055 * @cfg {String} text to display on mask (default Loading)
15059 * @cfg {Boolean} multiSelect Allow multiple selection
15061 multiSelect : false,
15063 * @cfg {Boolean} singleSelect Allow single selection
15065 singleSelect: false,
15068 * @cfg {Boolean} toggleSelect - selecting
15070 toggleSelect : false,
15073 * @cfg {Boolean} tickable - selecting
15078 * Returns the element this view is bound to.
15079 * @return {Roo.Element}
15081 getEl : function(){
15082 return this.wrapEl;
15088 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15090 refresh : function(){
15091 //Roo.log('refresh');
15094 // if we are using something like 'domtemplate', then
15095 // the what gets used is:
15096 // t.applySubtemplate(NAME, data, wrapping data..)
15097 // the outer template then get' applied with
15098 // the store 'extra data'
15099 // and the body get's added to the
15100 // roo-name="data" node?
15101 // <span class='roo-tpl-{name}'></span> ?????
15105 this.clearSelections();
15106 this.el.update("");
15108 var records = this.store.getRange();
15109 if(records.length < 1) {
15111 // is this valid?? = should it render a template??
15113 this.el.update(this.emptyText);
15117 if (this.dataName) {
15118 this.el.update(t.apply(this.store.meta)); //????
15119 el = this.el.child('.roo-tpl-' + this.dataName);
15122 for(var i = 0, len = records.length; i < len; i++){
15123 var data = this.prepareData(records[i].data, i, records[i]);
15124 this.fireEvent("preparedata", this, data, i, records[i]);
15126 var d = Roo.apply({}, data);
15129 Roo.apply(d, {'roo-id' : Roo.id()});
15133 Roo.each(this.parent.item, function(item){
15134 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15137 Roo.apply(d, {'roo-data-checked' : 'checked'});
15141 html[html.length] = Roo.util.Format.trim(
15143 t.applySubtemplate(this.dataName, d, this.store.meta) :
15150 el.update(html.join(""));
15151 this.nodes = el.dom.childNodes;
15152 this.updateIndexes(0);
15157 * Function to override to reformat the data that is sent to
15158 * the template for each node.
15159 * DEPRICATED - use the preparedata event handler.
15160 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15161 * a JSON object for an UpdateManager bound view).
15163 prepareData : function(data, index, record)
15165 this.fireEvent("preparedata", this, data, index, record);
15169 onUpdate : function(ds, record){
15170 // Roo.log('on update');
15171 this.clearSelections();
15172 var index = this.store.indexOf(record);
15173 var n = this.nodes[index];
15174 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15175 n.parentNode.removeChild(n);
15176 this.updateIndexes(index, index);
15182 onAdd : function(ds, records, index)
15184 //Roo.log(['on Add', ds, records, index] );
15185 this.clearSelections();
15186 if(this.nodes.length == 0){
15190 var n = this.nodes[index];
15191 for(var i = 0, len = records.length; i < len; i++){
15192 var d = this.prepareData(records[i].data, i, records[i]);
15194 this.tpl.insertBefore(n, d);
15197 this.tpl.append(this.el, d);
15200 this.updateIndexes(index);
15203 onRemove : function(ds, record, index){
15204 // Roo.log('onRemove');
15205 this.clearSelections();
15206 var el = this.dataName ?
15207 this.el.child('.roo-tpl-' + this.dataName) :
15210 el.dom.removeChild(this.nodes[index]);
15211 this.updateIndexes(index);
15215 * Refresh an individual node.
15216 * @param {Number} index
15218 refreshNode : function(index){
15219 this.onUpdate(this.store, this.store.getAt(index));
15222 updateIndexes : function(startIndex, endIndex){
15223 var ns = this.nodes;
15224 startIndex = startIndex || 0;
15225 endIndex = endIndex || ns.length - 1;
15226 for(var i = startIndex; i <= endIndex; i++){
15227 ns[i].nodeIndex = i;
15232 * Changes the data store this view uses and refresh the view.
15233 * @param {Store} store
15235 setStore : function(store, initial){
15236 if(!initial && this.store){
15237 this.store.un("datachanged", this.refresh);
15238 this.store.un("add", this.onAdd);
15239 this.store.un("remove", this.onRemove);
15240 this.store.un("update", this.onUpdate);
15241 this.store.un("clear", this.refresh);
15242 this.store.un("beforeload", this.onBeforeLoad);
15243 this.store.un("load", this.onLoad);
15244 this.store.un("loadexception", this.onLoad);
15248 store.on("datachanged", this.refresh, this);
15249 store.on("add", this.onAdd, this);
15250 store.on("remove", this.onRemove, this);
15251 store.on("update", this.onUpdate, this);
15252 store.on("clear", this.refresh, this);
15253 store.on("beforeload", this.onBeforeLoad, this);
15254 store.on("load", this.onLoad, this);
15255 store.on("loadexception", this.onLoad, this);
15263 * onbeforeLoad - masks the loading area.
15266 onBeforeLoad : function(store,opts)
15268 //Roo.log('onBeforeLoad');
15270 this.el.update("");
15272 this.el.mask(this.mask ? this.mask : "Loading" );
15274 onLoad : function ()
15281 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15282 * @param {HTMLElement} node
15283 * @return {HTMLElement} The template node
15285 findItemFromChild : function(node){
15286 var el = this.dataName ?
15287 this.el.child('.roo-tpl-' + this.dataName,true) :
15290 if(!node || node.parentNode == el){
15293 var p = node.parentNode;
15294 while(p && p != el){
15295 if(p.parentNode == el){
15304 onClick : function(e){
15305 var item = this.findItemFromChild(e.getTarget());
15307 var index = this.indexOf(item);
15308 if(this.onItemClick(item, index, e) !== false){
15309 this.fireEvent("click", this, index, item, e);
15312 this.clearSelections();
15317 onContextMenu : function(e){
15318 var item = this.findItemFromChild(e.getTarget());
15320 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15325 onDblClick : function(e){
15326 var item = this.findItemFromChild(e.getTarget());
15328 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15332 onItemClick : function(item, index, e)
15334 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15337 if (this.toggleSelect) {
15338 var m = this.isSelected(item) ? 'unselect' : 'select';
15341 _t[m](item, true, false);
15344 if(this.multiSelect || this.singleSelect){
15345 if(this.multiSelect && e.shiftKey && this.lastSelection){
15346 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15348 this.select(item, this.multiSelect && e.ctrlKey);
15349 this.lastSelection = item;
15352 if(!this.tickable){
15353 e.preventDefault();
15361 * Get the number of selected nodes.
15364 getSelectionCount : function(){
15365 return this.selections.length;
15369 * Get the currently selected nodes.
15370 * @return {Array} An array of HTMLElements
15372 getSelectedNodes : function(){
15373 return this.selections;
15377 * Get the indexes of the selected nodes.
15380 getSelectedIndexes : function(){
15381 var indexes = [], s = this.selections;
15382 for(var i = 0, len = s.length; i < len; i++){
15383 indexes.push(s[i].nodeIndex);
15389 * Clear all selections
15390 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15392 clearSelections : function(suppressEvent){
15393 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15394 this.cmp.elements = this.selections;
15395 this.cmp.removeClass(this.selectedClass);
15396 this.selections = [];
15397 if(!suppressEvent){
15398 this.fireEvent("selectionchange", this, this.selections);
15404 * Returns true if the passed node is selected
15405 * @param {HTMLElement/Number} node The node or node index
15406 * @return {Boolean}
15408 isSelected : function(node){
15409 var s = this.selections;
15413 node = this.getNode(node);
15414 return s.indexOf(node) !== -1;
15419 * @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
15420 * @param {Boolean} keepExisting (optional) true to keep existing selections
15421 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15423 select : function(nodeInfo, keepExisting, suppressEvent){
15424 if(nodeInfo instanceof Array){
15426 this.clearSelections(true);
15428 for(var i = 0, len = nodeInfo.length; i < len; i++){
15429 this.select(nodeInfo[i], true, true);
15433 var node = this.getNode(nodeInfo);
15434 if(!node || this.isSelected(node)){
15435 return; // already selected.
15438 this.clearSelections(true);
15441 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15442 Roo.fly(node).addClass(this.selectedClass);
15443 this.selections.push(node);
15444 if(!suppressEvent){
15445 this.fireEvent("selectionchange", this, this.selections);
15453 * @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
15454 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15455 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15457 unselect : function(nodeInfo, keepExisting, suppressEvent)
15459 if(nodeInfo instanceof Array){
15460 Roo.each(this.selections, function(s) {
15461 this.unselect(s, nodeInfo);
15465 var node = this.getNode(nodeInfo);
15466 if(!node || !this.isSelected(node)){
15467 //Roo.log("not selected");
15468 return; // not selected.
15472 Roo.each(this.selections, function(s) {
15474 Roo.fly(node).removeClass(this.selectedClass);
15481 this.selections= ns;
15482 this.fireEvent("selectionchange", this, this.selections);
15486 * Gets a template node.
15487 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15488 * @return {HTMLElement} The node or null if it wasn't found
15490 getNode : function(nodeInfo){
15491 if(typeof nodeInfo == "string"){
15492 return document.getElementById(nodeInfo);
15493 }else if(typeof nodeInfo == "number"){
15494 return this.nodes[nodeInfo];
15500 * Gets a range template nodes.
15501 * @param {Number} startIndex
15502 * @param {Number} endIndex
15503 * @return {Array} An array of nodes
15505 getNodes : function(start, end){
15506 var ns = this.nodes;
15507 start = start || 0;
15508 end = typeof end == "undefined" ? ns.length - 1 : end;
15511 for(var i = start; i <= end; i++){
15515 for(var i = start; i >= end; i--){
15523 * Finds the index of the passed node
15524 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15525 * @return {Number} The index of the node or -1
15527 indexOf : function(node){
15528 node = this.getNode(node);
15529 if(typeof node.nodeIndex == "number"){
15530 return node.nodeIndex;
15532 var ns = this.nodes;
15533 for(var i = 0, len = ns.length; i < len; i++){
15544 * based on jquery fullcalendar
15548 Roo.bootstrap = Roo.bootstrap || {};
15550 * @class Roo.bootstrap.Calendar
15551 * @extends Roo.bootstrap.Component
15552 * Bootstrap Calendar class
15553 * @cfg {Boolean} loadMask (true|false) default false
15554 * @cfg {Object} header generate the user specific header of the calendar, default false
15557 * Create a new Container
15558 * @param {Object} config The config object
15563 Roo.bootstrap.Calendar = function(config){
15564 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15568 * Fires when a date is selected
15569 * @param {DatePicker} this
15570 * @param {Date} date The selected date
15574 * @event monthchange
15575 * Fires when the displayed month changes
15576 * @param {DatePicker} this
15577 * @param {Date} date The selected month
15579 'monthchange': true,
15581 * @event evententer
15582 * Fires when mouse over an event
15583 * @param {Calendar} this
15584 * @param {event} Event
15586 'evententer': true,
15588 * @event eventleave
15589 * Fires when the mouse leaves an
15590 * @param {Calendar} this
15593 'eventleave': true,
15595 * @event eventclick
15596 * Fires when the mouse click an
15597 * @param {Calendar} this
15606 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15609 * @cfg {Number} startDay
15610 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15618 getAutoCreate : function(){
15621 var fc_button = function(name, corner, style, content ) {
15622 return Roo.apply({},{
15624 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15626 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15629 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15640 style : 'width:100%',
15647 cls : 'fc-header-left',
15649 fc_button('prev', 'left', 'arrow', '‹' ),
15650 fc_button('next', 'right', 'arrow', '›' ),
15651 { tag: 'span', cls: 'fc-header-space' },
15652 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15660 cls : 'fc-header-center',
15664 cls: 'fc-header-title',
15667 html : 'month / year'
15675 cls : 'fc-header-right',
15677 /* fc_button('month', 'left', '', 'month' ),
15678 fc_button('week', '', '', 'week' ),
15679 fc_button('day', 'right', '', 'day' )
15691 header = this.header;
15694 var cal_heads = function() {
15696 // fixme - handle this.
15698 for (var i =0; i < Date.dayNames.length; i++) {
15699 var d = Date.dayNames[i];
15702 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15703 html : d.substring(0,3)
15707 ret[0].cls += ' fc-first';
15708 ret[6].cls += ' fc-last';
15711 var cal_cell = function(n) {
15714 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15719 cls: 'fc-day-number',
15723 cls: 'fc-day-content',
15727 style: 'position: relative;' // height: 17px;
15739 var cal_rows = function() {
15742 for (var r = 0; r < 6; r++) {
15749 for (var i =0; i < Date.dayNames.length; i++) {
15750 var d = Date.dayNames[i];
15751 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15754 row.cn[0].cls+=' fc-first';
15755 row.cn[0].cn[0].style = 'min-height:90px';
15756 row.cn[6].cls+=' fc-last';
15760 ret[0].cls += ' fc-first';
15761 ret[4].cls += ' fc-prev-last';
15762 ret[5].cls += ' fc-last';
15769 cls: 'fc-border-separate',
15770 style : 'width:100%',
15778 cls : 'fc-first fc-last',
15796 cls : 'fc-content',
15797 style : "position: relative;",
15800 cls : 'fc-view fc-view-month fc-grid',
15801 style : 'position: relative',
15802 unselectable : 'on',
15805 cls : 'fc-event-container',
15806 style : 'position:absolute;z-index:8;top:0;left:0;'
15824 initEvents : function()
15827 throw "can not find store for calendar";
15833 style: "text-align:center",
15837 style: "background-color:white;width:50%;margin:250 auto",
15841 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15852 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15854 var size = this.el.select('.fc-content', true).first().getSize();
15855 this.maskEl.setSize(size.width, size.height);
15856 this.maskEl.enableDisplayMode("block");
15857 if(!this.loadMask){
15858 this.maskEl.hide();
15861 this.store = Roo.factory(this.store, Roo.data);
15862 this.store.on('load', this.onLoad, this);
15863 this.store.on('beforeload', this.onBeforeLoad, this);
15867 this.cells = this.el.select('.fc-day',true);
15868 //Roo.log(this.cells);
15869 this.textNodes = this.el.query('.fc-day-number');
15870 this.cells.addClassOnOver('fc-state-hover');
15872 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15873 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15874 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15875 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15877 this.on('monthchange', this.onMonthChange, this);
15879 this.update(new Date().clearTime());
15882 resize : function() {
15883 var sz = this.el.getSize();
15885 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15886 this.el.select('.fc-day-content div',true).setHeight(34);
15891 showPrevMonth : function(e){
15892 this.update(this.activeDate.add("mo", -1));
15894 showToday : function(e){
15895 this.update(new Date().clearTime());
15898 showNextMonth : function(e){
15899 this.update(this.activeDate.add("mo", 1));
15903 showPrevYear : function(){
15904 this.update(this.activeDate.add("y", -1));
15908 showNextYear : function(){
15909 this.update(this.activeDate.add("y", 1));
15914 update : function(date)
15916 var vd = this.activeDate;
15917 this.activeDate = date;
15918 // if(vd && this.el){
15919 // var t = date.getTime();
15920 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15921 // Roo.log('using add remove');
15923 // this.fireEvent('monthchange', this, date);
15925 // this.cells.removeClass("fc-state-highlight");
15926 // this.cells.each(function(c){
15927 // if(c.dateValue == t){
15928 // c.addClass("fc-state-highlight");
15929 // setTimeout(function(){
15930 // try{c.dom.firstChild.focus();}catch(e){}
15940 var days = date.getDaysInMonth();
15942 var firstOfMonth = date.getFirstDateOfMonth();
15943 var startingPos = firstOfMonth.getDay()-this.startDay;
15945 if(startingPos < this.startDay){
15949 var pm = date.add(Date.MONTH, -1);
15950 var prevStart = pm.getDaysInMonth()-startingPos;
15952 this.cells = this.el.select('.fc-day',true);
15953 this.textNodes = this.el.query('.fc-day-number');
15954 this.cells.addClassOnOver('fc-state-hover');
15956 var cells = this.cells.elements;
15957 var textEls = this.textNodes;
15959 Roo.each(cells, function(cell){
15960 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15963 days += startingPos;
15965 // convert everything to numbers so it's fast
15966 var day = 86400000;
15967 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15970 //Roo.log(prevStart);
15972 var today = new Date().clearTime().getTime();
15973 var sel = date.clearTime().getTime();
15974 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15975 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15976 var ddMatch = this.disabledDatesRE;
15977 var ddText = this.disabledDatesText;
15978 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15979 var ddaysText = this.disabledDaysText;
15980 var format = this.format;
15982 var setCellClass = function(cal, cell){
15986 //Roo.log('set Cell Class');
15988 var t = d.getTime();
15992 cell.dateValue = t;
15994 cell.className += " fc-today";
15995 cell.className += " fc-state-highlight";
15996 cell.title = cal.todayText;
15999 // disable highlight in other month..
16000 //cell.className += " fc-state-highlight";
16005 cell.className = " fc-state-disabled";
16006 cell.title = cal.minText;
16010 cell.className = " fc-state-disabled";
16011 cell.title = cal.maxText;
16015 if(ddays.indexOf(d.getDay()) != -1){
16016 cell.title = ddaysText;
16017 cell.className = " fc-state-disabled";
16020 if(ddMatch && format){
16021 var fvalue = d.dateFormat(format);
16022 if(ddMatch.test(fvalue)){
16023 cell.title = ddText.replace("%0", fvalue);
16024 cell.className = " fc-state-disabled";
16028 if (!cell.initialClassName) {
16029 cell.initialClassName = cell.dom.className;
16032 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16037 for(; i < startingPos; i++) {
16038 textEls[i].innerHTML = (++prevStart);
16039 d.setDate(d.getDate()+1);
16041 cells[i].className = "fc-past fc-other-month";
16042 setCellClass(this, cells[i]);
16047 for(; i < days; i++){
16048 intDay = i - startingPos + 1;
16049 textEls[i].innerHTML = (intDay);
16050 d.setDate(d.getDate()+1);
16052 cells[i].className = ''; // "x-date-active";
16053 setCellClass(this, cells[i]);
16057 for(; i < 42; i++) {
16058 textEls[i].innerHTML = (++extraDays);
16059 d.setDate(d.getDate()+1);
16061 cells[i].className = "fc-future fc-other-month";
16062 setCellClass(this, cells[i]);
16065 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16067 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16069 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16070 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16072 if(totalRows != 6){
16073 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16074 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16077 this.fireEvent('monthchange', this, date);
16081 if(!this.internalRender){
16082 var main = this.el.dom.firstChild;
16083 var w = main.offsetWidth;
16084 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16085 Roo.fly(main).setWidth(w);
16086 this.internalRender = true;
16087 // opera does not respect the auto grow header center column
16088 // then, after it gets a width opera refuses to recalculate
16089 // without a second pass
16090 if(Roo.isOpera && !this.secondPass){
16091 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16092 this.secondPass = true;
16093 this.update.defer(10, this, [date]);
16100 findCell : function(dt) {
16101 dt = dt.clearTime().getTime();
16103 this.cells.each(function(c){
16104 //Roo.log("check " +c.dateValue + '?=' + dt);
16105 if(c.dateValue == dt){
16115 findCells : function(ev) {
16116 var s = ev.start.clone().clearTime().getTime();
16118 var e= ev.end.clone().clearTime().getTime();
16121 this.cells.each(function(c){
16122 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16124 if(c.dateValue > e){
16127 if(c.dateValue < s){
16136 // findBestRow: function(cells)
16140 // for (var i =0 ; i < cells.length;i++) {
16141 // ret = Math.max(cells[i].rows || 0,ret);
16148 addItem : function(ev)
16150 // look for vertical location slot in
16151 var cells = this.findCells(ev);
16153 // ev.row = this.findBestRow(cells);
16155 // work out the location.
16159 for(var i =0; i < cells.length; i++) {
16161 cells[i].row = cells[0].row;
16164 cells[i].row = cells[i].row + 1;
16174 if (crow.start.getY() == cells[i].getY()) {
16176 crow.end = cells[i];
16193 cells[0].events.push(ev);
16195 this.calevents.push(ev);
16198 clearEvents: function() {
16200 if(!this.calevents){
16204 Roo.each(this.cells.elements, function(c){
16210 Roo.each(this.calevents, function(e) {
16211 Roo.each(e.els, function(el) {
16212 el.un('mouseenter' ,this.onEventEnter, this);
16213 el.un('mouseleave' ,this.onEventLeave, this);
16218 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16224 renderEvents: function()
16228 this.cells.each(function(c) {
16237 if(c.row != c.events.length){
16238 r = 4 - (4 - (c.row - c.events.length));
16241 c.events = ev.slice(0, r);
16242 c.more = ev.slice(r);
16244 if(c.more.length && c.more.length == 1){
16245 c.events.push(c.more.pop());
16248 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16252 this.cells.each(function(c) {
16254 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16257 for (var e = 0; e < c.events.length; e++){
16258 var ev = c.events[e];
16259 var rows = ev.rows;
16261 for(var i = 0; i < rows.length; i++) {
16263 // how many rows should it span..
16266 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16267 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16269 unselectable : "on",
16272 cls: 'fc-event-inner',
16276 // cls: 'fc-event-time',
16277 // html : cells.length > 1 ? '' : ev.time
16281 cls: 'fc-event-title',
16282 html : String.format('{0}', ev.title)
16289 cls: 'ui-resizable-handle ui-resizable-e',
16290 html : '  '
16297 cfg.cls += ' fc-event-start';
16299 if ((i+1) == rows.length) {
16300 cfg.cls += ' fc-event-end';
16303 var ctr = _this.el.select('.fc-event-container',true).first();
16304 var cg = ctr.createChild(cfg);
16306 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16307 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16309 var r = (c.more.length) ? 1 : 0;
16310 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16311 cg.setWidth(ebox.right - sbox.x -2);
16313 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16314 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16315 cg.on('click', _this.onEventClick, _this, ev);
16326 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16327 style : 'position: absolute',
16328 unselectable : "on",
16331 cls: 'fc-event-inner',
16335 cls: 'fc-event-title',
16343 cls: 'ui-resizable-handle ui-resizable-e',
16344 html : '  '
16350 var ctr = _this.el.select('.fc-event-container',true).first();
16351 var cg = ctr.createChild(cfg);
16353 var sbox = c.select('.fc-day-content',true).first().getBox();
16354 var ebox = c.select('.fc-day-content',true).first().getBox();
16356 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16357 cg.setWidth(ebox.right - sbox.x -2);
16359 cg.on('click', _this.onMoreEventClick, _this, c.more);
16369 onEventEnter: function (e, el,event,d) {
16370 this.fireEvent('evententer', this, el, event);
16373 onEventLeave: function (e, el,event,d) {
16374 this.fireEvent('eventleave', this, el, event);
16377 onEventClick: function (e, el,event,d) {
16378 this.fireEvent('eventclick', this, el, event);
16381 onMonthChange: function () {
16385 onMoreEventClick: function(e, el, more)
16389 this.calpopover.placement = 'right';
16390 this.calpopover.setTitle('More');
16392 this.calpopover.setContent('');
16394 var ctr = this.calpopover.el.select('.popover-content', true).first();
16396 Roo.each(more, function(m){
16398 cls : 'fc-event-hori fc-event-draggable',
16401 var cg = ctr.createChild(cfg);
16403 cg.on('click', _this.onEventClick, _this, m);
16406 this.calpopover.show(el);
16411 onLoad: function ()
16413 this.calevents = [];
16416 if(this.store.getCount() > 0){
16417 this.store.data.each(function(d){
16420 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16421 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16422 time : d.data.start_time,
16423 title : d.data.title,
16424 description : d.data.description,
16425 venue : d.data.venue
16430 this.renderEvents();
16432 if(this.calevents.length && this.loadMask){
16433 this.maskEl.hide();
16437 onBeforeLoad: function()
16439 this.clearEvents();
16441 this.maskEl.show();
16455 * @class Roo.bootstrap.Popover
16456 * @extends Roo.bootstrap.Component
16457 * Bootstrap Popover class
16458 * @cfg {String} html contents of the popover (or false to use children..)
16459 * @cfg {String} title of popover (or false to hide)
16460 * @cfg {String} placement how it is placed
16461 * @cfg {String} trigger click || hover (or false to trigger manually)
16462 * @cfg {String} over what (parent or false to trigger manually.)
16463 * @cfg {Number} delay - delay before showing
16466 * Create a new Popover
16467 * @param {Object} config The config object
16470 Roo.bootstrap.Popover = function(config){
16471 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16477 * After the popover show
16479 * @param {Roo.bootstrap.Popover} this
16484 * After the popover hide
16486 * @param {Roo.bootstrap.Popover} this
16492 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16494 title: 'Fill in a title',
16497 placement : 'right',
16498 trigger : 'hover', // hover
16504 can_build_overlaid : false,
16506 getChildContainer : function()
16508 return this.el.select('.popover-content',true).first();
16511 getAutoCreate : function(){
16514 cls : 'popover roo-dynamic',
16515 style: 'display:block',
16521 cls : 'popover-inner',
16525 cls: 'popover-title',
16529 cls : 'popover-content',
16540 setTitle: function(str)
16543 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16545 setContent: function(str)
16548 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16550 // as it get's added to the bottom of the page.
16551 onRender : function(ct, position)
16553 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16555 var cfg = Roo.apply({}, this.getAutoCreate());
16559 cfg.cls += ' ' + this.cls;
16562 cfg.style = this.style;
16564 //Roo.log("adding to ");
16565 this.el = Roo.get(document.body).createChild(cfg, position);
16566 // Roo.log(this.el);
16571 initEvents : function()
16573 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16574 this.el.enableDisplayMode('block');
16576 if (this.over === false) {
16579 if (this.triggers === false) {
16582 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16583 var triggers = this.trigger ? this.trigger.split(' ') : [];
16584 Roo.each(triggers, function(trigger) {
16586 if (trigger == 'click') {
16587 on_el.on('click', this.toggle, this);
16588 } else if (trigger != 'manual') {
16589 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16590 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16592 on_el.on(eventIn ,this.enter, this);
16593 on_el.on(eventOut, this.leave, this);
16604 toggle : function () {
16605 this.hoverState == 'in' ? this.leave() : this.enter();
16608 enter : function () {
16610 clearTimeout(this.timeout);
16612 this.hoverState = 'in';
16614 if (!this.delay || !this.delay.show) {
16619 this.timeout = setTimeout(function () {
16620 if (_t.hoverState == 'in') {
16623 }, this.delay.show)
16626 leave : function() {
16627 clearTimeout(this.timeout);
16629 this.hoverState = 'out';
16631 if (!this.delay || !this.delay.hide) {
16636 this.timeout = setTimeout(function () {
16637 if (_t.hoverState == 'out') {
16640 }, this.delay.hide)
16643 show : function (on_el)
16646 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16650 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16651 if (this.html !== false) {
16652 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16654 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16655 if (!this.title.length) {
16656 this.el.select('.popover-title',true).hide();
16659 var placement = typeof this.placement == 'function' ?
16660 this.placement.call(this, this.el, on_el) :
16663 var autoToken = /\s?auto?\s?/i;
16664 var autoPlace = autoToken.test(placement);
16666 placement = placement.replace(autoToken, '') || 'top';
16670 //this.el.setXY([0,0]);
16672 this.el.dom.style.display='block';
16673 this.el.addClass(placement);
16675 //this.el.appendTo(on_el);
16677 var p = this.getPosition();
16678 var box = this.el.getBox();
16683 var align = Roo.bootstrap.Popover.alignment[placement];
16684 this.el.alignTo(on_el, align[0],align[1]);
16685 //var arrow = this.el.select('.arrow',true).first();
16686 //arrow.set(align[2],
16688 this.el.addClass('in');
16691 if (this.el.hasClass('fade')) {
16695 this.hoverState = 'in';
16697 this.fireEvent('show', this);
16702 this.el.setXY([0,0]);
16703 this.el.removeClass('in');
16705 this.hoverState = null;
16707 this.fireEvent('hide', this);
16712 Roo.bootstrap.Popover.alignment = {
16713 'left' : ['r-l', [-10,0], 'right'],
16714 'right' : ['l-r', [10,0], 'left'],
16715 'bottom' : ['t-b', [0,10], 'top'],
16716 'top' : [ 'b-t', [0,-10], 'bottom']
16727 * @class Roo.bootstrap.Progress
16728 * @extends Roo.bootstrap.Component
16729 * Bootstrap Progress class
16730 * @cfg {Boolean} striped striped of the progress bar
16731 * @cfg {Boolean} active animated of the progress bar
16735 * Create a new Progress
16736 * @param {Object} config The config object
16739 Roo.bootstrap.Progress = function(config){
16740 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16743 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16748 getAutoCreate : function(){
16756 cfg.cls += ' progress-striped';
16760 cfg.cls += ' active';
16779 * @class Roo.bootstrap.ProgressBar
16780 * @extends Roo.bootstrap.Component
16781 * Bootstrap ProgressBar class
16782 * @cfg {Number} aria_valuenow aria-value now
16783 * @cfg {Number} aria_valuemin aria-value min
16784 * @cfg {Number} aria_valuemax aria-value max
16785 * @cfg {String} label label for the progress bar
16786 * @cfg {String} panel (success | info | warning | danger )
16787 * @cfg {String} role role of the progress bar
16788 * @cfg {String} sr_only text
16792 * Create a new ProgressBar
16793 * @param {Object} config The config object
16796 Roo.bootstrap.ProgressBar = function(config){
16797 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16800 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16804 aria_valuemax : 100,
16810 getAutoCreate : function()
16815 cls: 'progress-bar',
16816 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16828 cfg.role = this.role;
16831 if(this.aria_valuenow){
16832 cfg['aria-valuenow'] = this.aria_valuenow;
16835 if(this.aria_valuemin){
16836 cfg['aria-valuemin'] = this.aria_valuemin;
16839 if(this.aria_valuemax){
16840 cfg['aria-valuemax'] = this.aria_valuemax;
16843 if(this.label && !this.sr_only){
16844 cfg.html = this.label;
16848 cfg.cls += ' progress-bar-' + this.panel;
16854 update : function(aria_valuenow)
16856 this.aria_valuenow = aria_valuenow;
16858 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16873 * @class Roo.bootstrap.TabGroup
16874 * @extends Roo.bootstrap.Column
16875 * Bootstrap Column class
16876 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16877 * @cfg {Boolean} carousel true to make the group behave like a carousel
16878 * @cfg {Boolean} bullets show bullets for the panels
16879 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16880 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16881 * @cfg {Boolean} showarrow (true|false) show arrow default true
16884 * Create a new TabGroup
16885 * @param {Object} config The config object
16888 Roo.bootstrap.TabGroup = function(config){
16889 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16891 this.navId = Roo.id();
16894 Roo.bootstrap.TabGroup.register(this);
16898 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16901 transition : false,
16906 slideOnTouch : false,
16909 getAutoCreate : function()
16911 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16913 cfg.cls += ' tab-content';
16915 if (this.carousel) {
16916 cfg.cls += ' carousel slide';
16919 cls : 'carousel-inner',
16923 if(this.bullets && !Roo.isTouch){
16926 cls : 'carousel-bullets',
16930 if(this.bullets_cls){
16931 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16938 cfg.cn[0].cn.push(bullets);
16941 if(this.showarrow){
16942 cfg.cn[0].cn.push({
16944 class : 'carousel-arrow',
16948 class : 'carousel-prev',
16952 class : 'fa fa-chevron-left'
16958 class : 'carousel-next',
16962 class : 'fa fa-chevron-right'
16975 initEvents: function()
16977 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16978 // this.el.on("touchstart", this.onTouchStart, this);
16981 if(this.autoslide){
16984 this.slideFn = window.setInterval(function() {
16985 _this.showPanelNext();
16989 if(this.showarrow){
16990 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16991 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16997 // onTouchStart : function(e, el, o)
16999 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17003 // this.showPanelNext();
17007 getChildContainer : function()
17009 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17013 * register a Navigation item
17014 * @param {Roo.bootstrap.NavItem} the navitem to add
17016 register : function(item)
17018 this.tabs.push( item);
17019 item.navId = this.navId; // not really needed..
17024 getActivePanel : function()
17027 Roo.each(this.tabs, function(t) {
17037 getPanelByName : function(n)
17040 Roo.each(this.tabs, function(t) {
17041 if (t.tabId == n) {
17049 indexOfPanel : function(p)
17052 Roo.each(this.tabs, function(t,i) {
17053 if (t.tabId == p.tabId) {
17062 * show a specific panel
17063 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17064 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17066 showPanel : function (pan)
17068 if(this.transition || typeof(pan) == 'undefined'){
17069 Roo.log("waiting for the transitionend");
17073 if (typeof(pan) == 'number') {
17074 pan = this.tabs[pan];
17077 if (typeof(pan) == 'string') {
17078 pan = this.getPanelByName(pan);
17081 var cur = this.getActivePanel();
17084 Roo.log('pan or acitve pan is undefined');
17088 if (pan.tabId == this.getActivePanel().tabId) {
17092 if (false === cur.fireEvent('beforedeactivate')) {
17096 if(this.bullets > 0 && !Roo.isTouch){
17097 this.setActiveBullet(this.indexOfPanel(pan));
17100 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17102 this.transition = true;
17103 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17104 var lr = dir == 'next' ? 'left' : 'right';
17105 pan.el.addClass(dir); // or prev
17106 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17107 cur.el.addClass(lr); // or right
17108 pan.el.addClass(lr);
17111 cur.el.on('transitionend', function() {
17112 Roo.log("trans end?");
17114 pan.el.removeClass([lr,dir]);
17115 pan.setActive(true);
17117 cur.el.removeClass([lr]);
17118 cur.setActive(false);
17120 _this.transition = false;
17122 }, this, { single: true } );
17127 cur.setActive(false);
17128 pan.setActive(true);
17133 showPanelNext : function()
17135 var i = this.indexOfPanel(this.getActivePanel());
17137 if (i >= this.tabs.length - 1 && !this.autoslide) {
17141 if (i >= this.tabs.length - 1 && this.autoslide) {
17145 this.showPanel(this.tabs[i+1]);
17148 showPanelPrev : function()
17150 var i = this.indexOfPanel(this.getActivePanel());
17152 if (i < 1 && !this.autoslide) {
17156 if (i < 1 && this.autoslide) {
17157 i = this.tabs.length;
17160 this.showPanel(this.tabs[i-1]);
17164 addBullet: function()
17166 if(!this.bullets || Roo.isTouch){
17169 var ctr = this.el.select('.carousel-bullets',true).first();
17170 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17171 var bullet = ctr.createChild({
17172 cls : 'bullet bullet-' + i
17173 },ctr.dom.lastChild);
17178 bullet.on('click', (function(e, el, o, ii, t){
17180 e.preventDefault();
17182 this.showPanel(ii);
17184 if(this.autoslide && this.slideFn){
17185 clearInterval(this.slideFn);
17186 this.slideFn = window.setInterval(function() {
17187 _this.showPanelNext();
17191 }).createDelegate(this, [i, bullet], true));
17196 setActiveBullet : function(i)
17202 Roo.each(this.el.select('.bullet', true).elements, function(el){
17203 el.removeClass('selected');
17206 var bullet = this.el.select('.bullet-' + i, true).first();
17212 bullet.addClass('selected');
17223 Roo.apply(Roo.bootstrap.TabGroup, {
17227 * register a Navigation Group
17228 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17230 register : function(navgrp)
17232 this.groups[navgrp.navId] = navgrp;
17236 * fetch a Navigation Group based on the navigation ID
17237 * if one does not exist , it will get created.
17238 * @param {string} the navgroup to add
17239 * @returns {Roo.bootstrap.NavGroup} the navgroup
17241 get: function(navId) {
17242 if (typeof(this.groups[navId]) == 'undefined') {
17243 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17245 return this.groups[navId] ;
17260 * @class Roo.bootstrap.TabPanel
17261 * @extends Roo.bootstrap.Component
17262 * Bootstrap TabPanel class
17263 * @cfg {Boolean} active panel active
17264 * @cfg {String} html panel content
17265 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17266 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17267 * @cfg {String} href click to link..
17271 * Create a new TabPanel
17272 * @param {Object} config The config object
17275 Roo.bootstrap.TabPanel = function(config){
17276 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17280 * Fires when the active status changes
17281 * @param {Roo.bootstrap.TabPanel} this
17282 * @param {Boolean} state the new state
17287 * @event beforedeactivate
17288 * Fires before a tab is de-activated - can be used to do validation on a form.
17289 * @param {Roo.bootstrap.TabPanel} this
17290 * @return {Boolean} false if there is an error
17293 'beforedeactivate': true
17296 this.tabId = this.tabId || Roo.id();
17300 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17308 getAutoCreate : function(){
17311 // item is needed for carousel - not sure if it has any effect otherwise
17312 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17313 html: this.html || ''
17317 cfg.cls += ' active';
17321 cfg.tabId = this.tabId;
17328 initEvents: function()
17330 var p = this.parent();
17332 this.navId = this.navId || p.navId;
17334 if (typeof(this.navId) != 'undefined') {
17335 // not really needed.. but just in case.. parent should be a NavGroup.
17336 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17340 var i = tg.tabs.length - 1;
17342 if(this.active && tg.bullets > 0 && i < tg.bullets){
17343 tg.setActiveBullet(i);
17347 this.el.on('click', this.onClick, this);
17350 this.el.on("touchstart", this.onTouchStart, this);
17351 this.el.on("touchmove", this.onTouchMove, this);
17352 this.el.on("touchend", this.onTouchEnd, this);
17357 onRender : function(ct, position)
17359 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17362 setActive : function(state)
17364 Roo.log("panel - set active " + this.tabId + "=" + state);
17366 this.active = state;
17368 this.el.removeClass('active');
17370 } else if (!this.el.hasClass('active')) {
17371 this.el.addClass('active');
17374 this.fireEvent('changed', this, state);
17377 onClick : function(e)
17379 e.preventDefault();
17381 if(!this.href.length){
17385 window.location.href = this.href;
17394 onTouchStart : function(e)
17396 this.swiping = false;
17398 this.startX = e.browserEvent.touches[0].clientX;
17399 this.startY = e.browserEvent.touches[0].clientY;
17402 onTouchMove : function(e)
17404 this.swiping = true;
17406 this.endX = e.browserEvent.touches[0].clientX;
17407 this.endY = e.browserEvent.touches[0].clientY;
17410 onTouchEnd : function(e)
17417 var tabGroup = this.parent();
17419 if(this.endX > this.startX){ // swiping right
17420 tabGroup.showPanelPrev();
17424 if(this.startX > this.endX){ // swiping left
17425 tabGroup.showPanelNext();
17444 * @class Roo.bootstrap.DateField
17445 * @extends Roo.bootstrap.Input
17446 * Bootstrap DateField class
17447 * @cfg {Number} weekStart default 0
17448 * @cfg {String} viewMode default empty, (months|years)
17449 * @cfg {String} minViewMode default empty, (months|years)
17450 * @cfg {Number} startDate default -Infinity
17451 * @cfg {Number} endDate default Infinity
17452 * @cfg {Boolean} todayHighlight default false
17453 * @cfg {Boolean} todayBtn default false
17454 * @cfg {Boolean} calendarWeeks default false
17455 * @cfg {Object} daysOfWeekDisabled default empty
17456 * @cfg {Boolean} singleMode default false (true | false)
17458 * @cfg {Boolean} keyboardNavigation default true
17459 * @cfg {String} language default en
17462 * Create a new DateField
17463 * @param {Object} config The config object
17466 Roo.bootstrap.DateField = function(config){
17467 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17471 * Fires when this field show.
17472 * @param {Roo.bootstrap.DateField} this
17473 * @param {Mixed} date The date value
17478 * Fires when this field hide.
17479 * @param {Roo.bootstrap.DateField} this
17480 * @param {Mixed} date The date value
17485 * Fires when select a date.
17486 * @param {Roo.bootstrap.DateField} this
17487 * @param {Mixed} date The date value
17491 * @event beforeselect
17492 * Fires when before select a date.
17493 * @param {Roo.bootstrap.DateField} this
17494 * @param {Mixed} date The date value
17496 beforeselect : true
17500 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
17503 * @cfg {String} format
17504 * The default date format string which can be overriden for localization support. The format must be
17505 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17509 * @cfg {String} altFormats
17510 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17511 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17513 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17521 todayHighlight : false,
17527 keyboardNavigation: true,
17529 calendarWeeks: false,
17531 startDate: -Infinity,
17535 daysOfWeekDisabled: [],
17539 singleMode : false,
17541 UTCDate: function()
17543 return new Date(Date.UTC.apply(Date, arguments));
17546 UTCToday: function()
17548 var today = new Date();
17549 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17552 getDate: function() {
17553 var d = this.getUTCDate();
17554 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17557 getUTCDate: function() {
17561 setDate: function(d) {
17562 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17565 setUTCDate: function(d) {
17567 this.setValue(this.formatDate(this.date));
17570 onRender: function(ct, position)
17573 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17575 this.language = this.language || 'en';
17576 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17577 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17579 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17580 this.format = this.format || 'm/d/y';
17581 this.isInline = false;
17582 this.isInput = true;
17583 this.component = this.el.select('.add-on', true).first() || false;
17584 this.component = (this.component && this.component.length === 0) ? false : this.component;
17585 this.hasInput = this.component && this.inputEl().length;
17587 if (typeof(this.minViewMode === 'string')) {
17588 switch (this.minViewMode) {
17590 this.minViewMode = 1;
17593 this.minViewMode = 2;
17596 this.minViewMode = 0;
17601 if (typeof(this.viewMode === 'string')) {
17602 switch (this.viewMode) {
17615 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17617 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17619 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17621 this.picker().on('mousedown', this.onMousedown, this);
17622 this.picker().on('click', this.onClick, this);
17624 this.picker().addClass('datepicker-dropdown');
17626 this.startViewMode = this.viewMode;
17628 if(this.singleMode){
17629 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17630 v.setVisibilityMode(Roo.Element.DISPLAY);
17634 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17635 v.setStyle('width', '189px');
17639 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17640 if(!this.calendarWeeks){
17645 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17646 v.attr('colspan', function(i, val){
17647 return parseInt(val) + 1;
17652 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17654 this.setStartDate(this.startDate);
17655 this.setEndDate(this.endDate);
17657 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17664 if(this.isInline) {
17669 picker : function()
17671 return this.pickerEl;
17672 // return this.el.select('.datepicker', true).first();
17675 fillDow: function()
17677 var dowCnt = this.weekStart;
17686 if(this.calendarWeeks){
17694 while (dowCnt < this.weekStart + 7) {
17698 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17702 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17705 fillMonths: function()
17708 var months = this.picker().select('>.datepicker-months td', true).first();
17710 months.dom.innerHTML = '';
17716 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17719 months.createChild(month);
17726 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;
17728 if (this.date < this.startDate) {
17729 this.viewDate = new Date(this.startDate);
17730 } else if (this.date > this.endDate) {
17731 this.viewDate = new Date(this.endDate);
17733 this.viewDate = new Date(this.date);
17741 var d = new Date(this.viewDate),
17742 year = d.getUTCFullYear(),
17743 month = d.getUTCMonth(),
17744 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17745 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17746 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17747 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17748 currentDate = this.date && this.date.valueOf(),
17749 today = this.UTCToday();
17751 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17753 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17755 // this.picker.select('>tfoot th.today').
17756 // .text(dates[this.language].today)
17757 // .toggle(this.todayBtn !== false);
17759 this.updateNavArrows();
17762 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17764 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17766 prevMonth.setUTCDate(day);
17768 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17770 var nextMonth = new Date(prevMonth);
17772 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17774 nextMonth = nextMonth.valueOf();
17776 var fillMonths = false;
17778 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17780 while(prevMonth.valueOf() < nextMonth) {
17783 if (prevMonth.getUTCDay() === this.weekStart) {
17785 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17793 if(this.calendarWeeks){
17794 // ISO 8601: First week contains first thursday.
17795 // ISO also states week starts on Monday, but we can be more abstract here.
17797 // Start of current week: based on weekstart/current date
17798 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17799 // Thursday of this week
17800 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17801 // First Thursday of year, year from thursday
17802 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17803 // Calendar week: ms between thursdays, div ms per day, div 7 days
17804 calWeek = (th - yth) / 864e5 / 7 + 1;
17806 fillMonths.cn.push({
17814 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17816 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17819 if (this.todayHighlight &&
17820 prevMonth.getUTCFullYear() == today.getFullYear() &&
17821 prevMonth.getUTCMonth() == today.getMonth() &&
17822 prevMonth.getUTCDate() == today.getDate()) {
17823 clsName += ' today';
17826 if (currentDate && prevMonth.valueOf() === currentDate) {
17827 clsName += ' active';
17830 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17831 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17832 clsName += ' disabled';
17835 fillMonths.cn.push({
17837 cls: 'day ' + clsName,
17838 html: prevMonth.getDate()
17841 prevMonth.setDate(prevMonth.getDate()+1);
17844 var currentYear = this.date && this.date.getUTCFullYear();
17845 var currentMonth = this.date && this.date.getUTCMonth();
17847 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17849 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17850 v.removeClass('active');
17852 if(currentYear === year && k === currentMonth){
17853 v.addClass('active');
17856 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17857 v.addClass('disabled');
17863 year = parseInt(year/10, 10) * 10;
17865 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17867 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17870 for (var i = -1; i < 11; i++) {
17871 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17873 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17881 showMode: function(dir)
17884 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17887 Roo.each(this.picker().select('>div',true).elements, function(v){
17888 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17891 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17896 if(this.isInline) {
17900 this.picker().removeClass(['bottom', 'top']);
17902 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17904 * place to the top of element!
17908 this.picker().addClass('top');
17909 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17914 this.picker().addClass('bottom');
17916 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17919 parseDate : function(value)
17921 if(!value || value instanceof Date){
17924 var v = Date.parseDate(value, this.format);
17925 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17926 v = Date.parseDate(value, 'Y-m-d');
17928 if(!v && this.altFormats){
17929 if(!this.altFormatsArray){
17930 this.altFormatsArray = this.altFormats.split("|");
17932 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17933 v = Date.parseDate(value, this.altFormatsArray[i]);
17939 formatDate : function(date, fmt)
17941 return (!date || !(date instanceof Date)) ?
17942 date : date.dateFormat(fmt || this.format);
17945 onFocus : function()
17947 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17951 onBlur : function()
17953 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17955 var d = this.inputEl().getValue();
17964 this.picker().show();
17968 this.fireEvent('show', this, this.date);
17973 if(this.isInline) {
17976 this.picker().hide();
17977 this.viewMode = this.startViewMode;
17980 this.fireEvent('hide', this, this.date);
17984 onMousedown: function(e)
17986 e.stopPropagation();
17987 e.preventDefault();
17992 Roo.bootstrap.DateField.superclass.keyup.call(this);
17996 setValue: function(v)
17998 if(this.fireEvent('beforeselect', this, v) !== false){
17999 var d = new Date(this.parseDate(v) ).clearTime();
18001 if(isNaN(d.getTime())){
18002 this.date = this.viewDate = '';
18003 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18007 v = this.formatDate(d);
18009 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18011 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18015 this.fireEvent('select', this, this.date);
18019 getValue: function()
18021 return this.formatDate(this.date);
18024 fireKey: function(e)
18026 if (!this.picker().isVisible()){
18027 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18033 var dateChanged = false,
18035 newDate, newViewDate;
18040 e.preventDefault();
18044 if (!this.keyboardNavigation) {
18047 dir = e.keyCode == 37 ? -1 : 1;
18050 newDate = this.moveYear(this.date, dir);
18051 newViewDate = this.moveYear(this.viewDate, dir);
18052 } else if (e.shiftKey){
18053 newDate = this.moveMonth(this.date, dir);
18054 newViewDate = this.moveMonth(this.viewDate, dir);
18056 newDate = new Date(this.date);
18057 newDate.setUTCDate(this.date.getUTCDate() + dir);
18058 newViewDate = new Date(this.viewDate);
18059 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18061 if (this.dateWithinRange(newDate)){
18062 this.date = newDate;
18063 this.viewDate = newViewDate;
18064 this.setValue(this.formatDate(this.date));
18066 e.preventDefault();
18067 dateChanged = true;
18072 if (!this.keyboardNavigation) {
18075 dir = e.keyCode == 38 ? -1 : 1;
18077 newDate = this.moveYear(this.date, dir);
18078 newViewDate = this.moveYear(this.viewDate, dir);
18079 } else if (e.shiftKey){
18080 newDate = this.moveMonth(this.date, dir);
18081 newViewDate = this.moveMonth(this.viewDate, dir);
18083 newDate = new Date(this.date);
18084 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18085 newViewDate = new Date(this.viewDate);
18086 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18088 if (this.dateWithinRange(newDate)){
18089 this.date = newDate;
18090 this.viewDate = newViewDate;
18091 this.setValue(this.formatDate(this.date));
18093 e.preventDefault();
18094 dateChanged = true;
18098 this.setValue(this.formatDate(this.date));
18100 e.preventDefault();
18103 this.setValue(this.formatDate(this.date));
18117 onClick: function(e)
18119 e.stopPropagation();
18120 e.preventDefault();
18122 var target = e.getTarget();
18124 if(target.nodeName.toLowerCase() === 'i'){
18125 target = Roo.get(target).dom.parentNode;
18128 var nodeName = target.nodeName;
18129 var className = target.className;
18130 var html = target.innerHTML;
18131 //Roo.log(nodeName);
18133 switch(nodeName.toLowerCase()) {
18135 switch(className) {
18141 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18142 switch(this.viewMode){
18144 this.viewDate = this.moveMonth(this.viewDate, dir);
18148 this.viewDate = this.moveYear(this.viewDate, dir);
18154 var date = new Date();
18155 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18157 this.setValue(this.formatDate(this.date));
18164 if (className.indexOf('disabled') < 0) {
18165 this.viewDate.setUTCDate(1);
18166 if (className.indexOf('month') > -1) {
18167 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18169 var year = parseInt(html, 10) || 0;
18170 this.viewDate.setUTCFullYear(year);
18174 if(this.singleMode){
18175 this.setValue(this.formatDate(this.viewDate));
18186 //Roo.log(className);
18187 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18188 var day = parseInt(html, 10) || 1;
18189 var year = this.viewDate.getUTCFullYear(),
18190 month = this.viewDate.getUTCMonth();
18192 if (className.indexOf('old') > -1) {
18199 } else if (className.indexOf('new') > -1) {
18207 //Roo.log([year,month,day]);
18208 this.date = this.UTCDate(year, month, day,0,0,0,0);
18209 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18211 //Roo.log(this.formatDate(this.date));
18212 this.setValue(this.formatDate(this.date));
18219 setStartDate: function(startDate)
18221 this.startDate = startDate || -Infinity;
18222 if (this.startDate !== -Infinity) {
18223 this.startDate = this.parseDate(this.startDate);
18226 this.updateNavArrows();
18229 setEndDate: function(endDate)
18231 this.endDate = endDate || Infinity;
18232 if (this.endDate !== Infinity) {
18233 this.endDate = this.parseDate(this.endDate);
18236 this.updateNavArrows();
18239 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18241 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18242 if (typeof(this.daysOfWeekDisabled) !== 'object') {
18243 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18245 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18246 return parseInt(d, 10);
18249 this.updateNavArrows();
18252 updateNavArrows: function()
18254 if(this.singleMode){
18258 var d = new Date(this.viewDate),
18259 year = d.getUTCFullYear(),
18260 month = d.getUTCMonth();
18262 Roo.each(this.picker().select('.prev', true).elements, function(v){
18264 switch (this.viewMode) {
18267 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18273 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18280 Roo.each(this.picker().select('.next', true).elements, function(v){
18282 switch (this.viewMode) {
18285 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18291 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18299 moveMonth: function(date, dir)
18304 var new_date = new Date(date.valueOf()),
18305 day = new_date.getUTCDate(),
18306 month = new_date.getUTCMonth(),
18307 mag = Math.abs(dir),
18309 dir = dir > 0 ? 1 : -1;
18312 // If going back one month, make sure month is not current month
18313 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18315 return new_date.getUTCMonth() == month;
18317 // If going forward one month, make sure month is as expected
18318 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18320 return new_date.getUTCMonth() != new_month;
18322 new_month = month + dir;
18323 new_date.setUTCMonth(new_month);
18324 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18325 if (new_month < 0 || new_month > 11) {
18326 new_month = (new_month + 12) % 12;
18329 // For magnitudes >1, move one month at a time...
18330 for (var i=0; i<mag; i++) {
18331 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18332 new_date = this.moveMonth(new_date, dir);
18334 // ...then reset the day, keeping it in the new month
18335 new_month = new_date.getUTCMonth();
18336 new_date.setUTCDate(day);
18338 return new_month != new_date.getUTCMonth();
18341 // Common date-resetting loop -- if date is beyond end of month, make it
18344 new_date.setUTCDate(--day);
18345 new_date.setUTCMonth(new_month);
18350 moveYear: function(date, dir)
18352 return this.moveMonth(date, dir*12);
18355 dateWithinRange: function(date)
18357 return date >= this.startDate && date <= this.endDate;
18363 this.picker().remove();
18366 validateValue : function(value)
18368 if(value.length < 1) {
18369 if(this.allowBlank){
18375 if(value.length < this.minLength){
18378 if(value.length > this.maxLength){
18382 var vt = Roo.form.VTypes;
18383 if(!vt[this.vtype](value, this)){
18387 if(typeof this.validator == "function"){
18388 var msg = this.validator(value);
18394 if(this.regex && !this.regex.test(value)){
18398 if(typeof(this.parseDate(value)) == 'undefined'){
18402 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18406 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18416 Roo.apply(Roo.bootstrap.DateField, {
18427 html: '<i class="fa fa-arrow-left"/>'
18437 html: '<i class="fa fa-arrow-right"/>'
18479 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18480 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18481 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18482 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18483 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18496 navFnc: 'FullYear',
18501 navFnc: 'FullYear',
18506 Roo.apply(Roo.bootstrap.DateField, {
18510 cls: 'datepicker dropdown-menu roo-dynamic',
18514 cls: 'datepicker-days',
18518 cls: 'table-condensed',
18520 Roo.bootstrap.DateField.head,
18524 Roo.bootstrap.DateField.footer
18531 cls: 'datepicker-months',
18535 cls: 'table-condensed',
18537 Roo.bootstrap.DateField.head,
18538 Roo.bootstrap.DateField.content,
18539 Roo.bootstrap.DateField.footer
18546 cls: 'datepicker-years',
18550 cls: 'table-condensed',
18552 Roo.bootstrap.DateField.head,
18553 Roo.bootstrap.DateField.content,
18554 Roo.bootstrap.DateField.footer
18573 * @class Roo.bootstrap.TimeField
18574 * @extends Roo.bootstrap.Input
18575 * Bootstrap DateField class
18579 * Create a new TimeField
18580 * @param {Object} config The config object
18583 Roo.bootstrap.TimeField = function(config){
18584 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18588 * Fires when this field show.
18589 * @param {Roo.bootstrap.DateField} thisthis
18590 * @param {Mixed} date The date value
18595 * Fires when this field hide.
18596 * @param {Roo.bootstrap.DateField} this
18597 * @param {Mixed} date The date value
18602 * Fires when select a date.
18603 * @param {Roo.bootstrap.DateField} this
18604 * @param {Mixed} date The date value
18610 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18613 * @cfg {String} format
18614 * The default time format string which can be overriden for localization support. The format must be
18615 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18619 onRender: function(ct, position)
18622 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18624 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18626 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18628 this.pop = this.picker().select('>.datepicker-time',true).first();
18629 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18631 this.picker().on('mousedown', this.onMousedown, this);
18632 this.picker().on('click', this.onClick, this);
18634 this.picker().addClass('datepicker-dropdown');
18639 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18640 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18641 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18642 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18643 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18644 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18648 fireKey: function(e){
18649 if (!this.picker().isVisible()){
18650 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18656 e.preventDefault();
18664 this.onTogglePeriod();
18667 this.onIncrementMinutes();
18670 this.onDecrementMinutes();
18679 onClick: function(e) {
18680 e.stopPropagation();
18681 e.preventDefault();
18684 picker : function()
18686 return this.el.select('.datepicker', true).first();
18689 fillTime: function()
18691 var time = this.pop.select('tbody', true).first();
18693 time.dom.innerHTML = '';
18708 cls: 'hours-up glyphicon glyphicon-chevron-up'
18728 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18749 cls: 'timepicker-hour',
18764 cls: 'timepicker-minute',
18779 cls: 'btn btn-primary period',
18801 cls: 'hours-down glyphicon glyphicon-chevron-down'
18821 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18839 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18846 var hours = this.time.getHours();
18847 var minutes = this.time.getMinutes();
18860 hours = hours - 12;
18864 hours = '0' + hours;
18868 minutes = '0' + minutes;
18871 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18872 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18873 this.pop.select('button', true).first().dom.innerHTML = period;
18879 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18881 var cls = ['bottom'];
18883 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18890 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18895 this.picker().addClass(cls.join('-'));
18899 Roo.each(cls, function(c){
18901 _this.picker().setTop(_this.inputEl().getHeight());
18905 _this.picker().setTop(0 - _this.picker().getHeight());
18910 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18914 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18921 onFocus : function()
18923 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18927 onBlur : function()
18929 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18935 this.picker().show();
18940 this.fireEvent('show', this, this.date);
18945 this.picker().hide();
18948 this.fireEvent('hide', this, this.date);
18951 setTime : function()
18954 this.setValue(this.time.format(this.format));
18956 this.fireEvent('select', this, this.date);
18961 onMousedown: function(e){
18962 e.stopPropagation();
18963 e.preventDefault();
18966 onIncrementHours: function()
18968 Roo.log('onIncrementHours');
18969 this.time = this.time.add(Date.HOUR, 1);
18974 onDecrementHours: function()
18976 Roo.log('onDecrementHours');
18977 this.time = this.time.add(Date.HOUR, -1);
18981 onIncrementMinutes: function()
18983 Roo.log('onIncrementMinutes');
18984 this.time = this.time.add(Date.MINUTE, 1);
18988 onDecrementMinutes: function()
18990 Roo.log('onDecrementMinutes');
18991 this.time = this.time.add(Date.MINUTE, -1);
18995 onTogglePeriod: function()
18997 Roo.log('onTogglePeriod');
18998 this.time = this.time.add(Date.HOUR, 12);
19005 Roo.apply(Roo.bootstrap.TimeField, {
19035 cls: 'btn btn-info ok',
19047 Roo.apply(Roo.bootstrap.TimeField, {
19051 cls: 'datepicker dropdown-menu',
19055 cls: 'datepicker-time',
19059 cls: 'table-condensed',
19061 Roo.bootstrap.TimeField.content,
19062 Roo.bootstrap.TimeField.footer
19081 * @class Roo.bootstrap.MonthField
19082 * @extends Roo.bootstrap.Input
19083 * Bootstrap MonthField class
19085 * @cfg {String} language default en
19088 * Create a new MonthField
19089 * @param {Object} config The config object
19092 Roo.bootstrap.MonthField = function(config){
19093 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19098 * Fires when this field show.
19099 * @param {Roo.bootstrap.MonthField} this
19100 * @param {Mixed} date The date value
19105 * Fires when this field hide.
19106 * @param {Roo.bootstrap.MonthField} this
19107 * @param {Mixed} date The date value
19112 * Fires when select a date.
19113 * @param {Roo.bootstrap.MonthField} this
19114 * @param {String} oldvalue The old value
19115 * @param {String} newvalue The new value
19121 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
19123 onRender: function(ct, position)
19126 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19128 this.language = this.language || 'en';
19129 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19130 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19132 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19133 this.isInline = false;
19134 this.isInput = true;
19135 this.component = this.el.select('.add-on', true).first() || false;
19136 this.component = (this.component && this.component.length === 0) ? false : this.component;
19137 this.hasInput = this.component && this.inputEL().length;
19139 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19141 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19143 this.picker().on('mousedown', this.onMousedown, this);
19144 this.picker().on('click', this.onClick, this);
19146 this.picker().addClass('datepicker-dropdown');
19148 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19149 v.setStyle('width', '189px');
19156 if(this.isInline) {
19162 setValue: function(v, suppressEvent)
19164 var o = this.getValue();
19166 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19170 if(suppressEvent !== true){
19171 this.fireEvent('select', this, o, v);
19176 getValue: function()
19181 onClick: function(e)
19183 e.stopPropagation();
19184 e.preventDefault();
19186 var target = e.getTarget();
19188 if(target.nodeName.toLowerCase() === 'i'){
19189 target = Roo.get(target).dom.parentNode;
19192 var nodeName = target.nodeName;
19193 var className = target.className;
19194 var html = target.innerHTML;
19196 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19200 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19202 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19208 picker : function()
19210 return this.pickerEl;
19213 fillMonths: function()
19216 var months = this.picker().select('>.datepicker-months td', true).first();
19218 months.dom.innerHTML = '';
19224 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19227 months.createChild(month);
19236 if(typeof(this.vIndex) == 'undefined' && this.value.length){
19237 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19240 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19241 e.removeClass('active');
19243 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19244 e.addClass('active');
19251 if(this.isInline) {
19255 this.picker().removeClass(['bottom', 'top']);
19257 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19259 * place to the top of element!
19263 this.picker().addClass('top');
19264 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19269 this.picker().addClass('bottom');
19271 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19274 onFocus : function()
19276 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19280 onBlur : function()
19282 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19284 var d = this.inputEl().getValue();
19293 this.picker().show();
19294 this.picker().select('>.datepicker-months', true).first().show();
19298 this.fireEvent('show', this, this.date);
19303 if(this.isInline) {
19306 this.picker().hide();
19307 this.fireEvent('hide', this, this.date);
19311 onMousedown: function(e)
19313 e.stopPropagation();
19314 e.preventDefault();
19319 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19323 fireKey: function(e)
19325 if (!this.picker().isVisible()){
19326 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19337 e.preventDefault();
19341 dir = e.keyCode == 37 ? -1 : 1;
19343 this.vIndex = this.vIndex + dir;
19345 if(this.vIndex < 0){
19349 if(this.vIndex > 11){
19353 if(isNaN(this.vIndex)){
19357 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19363 dir = e.keyCode == 38 ? -1 : 1;
19365 this.vIndex = this.vIndex + dir * 4;
19367 if(this.vIndex < 0){
19371 if(this.vIndex > 11){
19375 if(isNaN(this.vIndex)){
19379 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19384 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19385 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19389 e.preventDefault();
19392 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19393 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19409 this.picker().remove();
19414 Roo.apply(Roo.bootstrap.MonthField, {
19433 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19434 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19439 Roo.apply(Roo.bootstrap.MonthField, {
19443 cls: 'datepicker dropdown-menu roo-dynamic',
19447 cls: 'datepicker-months',
19451 cls: 'table-condensed',
19453 Roo.bootstrap.DateField.content
19473 * @class Roo.bootstrap.CheckBox
19474 * @extends Roo.bootstrap.Input
19475 * Bootstrap CheckBox class
19477 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19478 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19479 * @cfg {String} boxLabel The text that appears beside the checkbox
19480 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19481 * @cfg {Boolean} checked initnal the element
19482 * @cfg {Boolean} inline inline the element (default false)
19483 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19486 * Create a new CheckBox
19487 * @param {Object} config The config object
19490 Roo.bootstrap.CheckBox = function(config){
19491 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19496 * Fires when the element is checked or unchecked.
19497 * @param {Roo.bootstrap.CheckBox} this This input
19498 * @param {Boolean} checked The new checked value
19505 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
19507 inputType: 'checkbox',
19515 getAutoCreate : function()
19517 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19523 cfg.cls = 'form-group ' + this.inputType; //input-group
19526 cfg.cls += ' ' + this.inputType + '-inline';
19532 type : this.inputType,
19533 value : this.inputValue,
19534 cls : 'roo-' + this.inputType, //'form-box',
19535 placeholder : this.placeholder || ''
19539 if(this.inputType != 'radio'){
19543 cls : 'roo-hidden-value',
19544 value : this.checked ? this.valueOff : this.inputValue
19549 if (this.weight) { // Validity check?
19550 cfg.cls += " " + this.inputType + "-" + this.weight;
19553 if (this.disabled) {
19554 input.disabled=true;
19558 input.checked = this.checked;
19565 input.name = this.name;
19567 if(this.inputType != 'radio'){
19568 hidden.name = this.name;
19569 input.name = '_hidden_' + this.name;
19574 input.cls += ' input-' + this.size;
19579 ['xs','sm','md','lg'].map(function(size){
19580 if (settings[size]) {
19581 cfg.cls += ' col-' + size + '-' + settings[size];
19585 var inputblock = input;
19587 if (this.before || this.after) {
19590 cls : 'input-group',
19595 inputblock.cn.push({
19597 cls : 'input-group-addon',
19602 inputblock.cn.push(input);
19604 if(this.inputType != 'radio'){
19605 inputblock.cn.push(hidden);
19609 inputblock.cn.push({
19611 cls : 'input-group-addon',
19618 if (align ==='left' && this.fieldLabel.length) {
19619 // Roo.log("left and has label");
19625 cls : 'control-label col-md-' + this.labelWidth,
19626 html : this.fieldLabel
19630 cls : "col-md-" + (12 - this.labelWidth),
19637 } else if ( this.fieldLabel.length) {
19638 // Roo.log(" label");
19642 tag: this.boxLabel ? 'span' : 'label',
19644 cls: 'control-label box-input-label',
19645 //cls : 'input-group-addon',
19646 html : this.fieldLabel
19656 // Roo.log(" no label && no align");
19657 cfg.cn = [ inputblock ] ;
19663 var boxLabelCfg = {
19665 //'for': id, // box label is handled by onclick - so no for...
19667 html: this.boxLabel
19671 boxLabelCfg.tooltip = this.tooltip;
19674 cfg.cn.push(boxLabelCfg);
19677 if(this.inputType != 'radio'){
19678 cfg.cn.push(hidden);
19686 * return the real input element.
19688 inputEl: function ()
19690 return this.el.select('input.roo-' + this.inputType,true).first();
19692 hiddenEl: function ()
19694 return this.el.select('input.roo-hidden-value',true).first();
19697 labelEl: function()
19699 return this.el.select('label.control-label',true).first();
19701 /* depricated... */
19705 return this.labelEl();
19708 boxLabelEl: function()
19710 return this.el.select('label.box-label',true).first();
19713 initEvents : function()
19715 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19717 this.inputEl().on('click', this.onClick, this);
19719 if (this.boxLabel) {
19720 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19723 this.startValue = this.getValue();
19726 Roo.bootstrap.CheckBox.register(this);
19730 onClick : function()
19732 this.setChecked(!this.checked);
19735 setChecked : function(state,suppressEvent)
19737 this.startValue = this.getValue();
19739 if(this.inputType == 'radio'){
19741 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19742 e.dom.checked = false;
19745 this.inputEl().dom.checked = true;
19747 this.inputEl().dom.value = this.inputValue;
19749 if(suppressEvent !== true){
19750 this.fireEvent('check', this, true);
19758 this.checked = state;
19760 this.inputEl().dom.checked = state;
19763 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19765 if(suppressEvent !== true){
19766 this.fireEvent('check', this, state);
19772 getValue : function()
19774 if(this.inputType == 'radio'){
19775 return this.getGroupValue();
19778 return this.hiddenEl().dom.value;
19782 getGroupValue : function()
19784 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19788 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19791 setValue : function(v,suppressEvent)
19793 if(this.inputType == 'radio'){
19794 this.setGroupValue(v, suppressEvent);
19798 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19803 setGroupValue : function(v, suppressEvent)
19805 this.startValue = this.getValue();
19807 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19808 e.dom.checked = false;
19810 if(e.dom.value == v){
19811 e.dom.checked = true;
19815 if(suppressEvent !== true){
19816 this.fireEvent('check', this, true);
19824 validate : function()
19828 (this.inputType == 'radio' && this.validateRadio()) ||
19829 (this.inputType == 'checkbox' && this.validateCheckbox())
19835 this.markInvalid();
19839 validateRadio : function()
19841 if(this.allowBlank){
19847 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19848 if(!e.dom.checked){
19860 validateCheckbox : function()
19863 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19866 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19874 for(var i in group){
19879 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19886 * Mark this field as valid
19888 markValid : function()
19890 if(this.allowBlank){
19896 this.fireEvent('valid', this);
19898 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19901 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19908 if(this.inputType == 'radio'){
19909 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19910 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19911 e.findParent('.form-group', false, true).addClass(_this.validClass);
19918 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19919 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19923 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19929 for(var i in group){
19930 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19931 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19936 * Mark this field as invalid
19937 * @param {String} msg The validation message
19939 markInvalid : function(msg)
19941 if(this.allowBlank){
19947 this.fireEvent('invalid', this, msg);
19949 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19952 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19956 label.markInvalid();
19959 if(this.inputType == 'radio'){
19960 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19961 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19962 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19969 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19970 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19974 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19980 for(var i in group){
19981 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19982 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19987 disable : function()
19989 if(this.inputType != 'radio'){
19990 Roo.bootstrap.CheckBox.superclass.disable.call(this);
19997 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19998 _this.getActionEl().addClass(this.disabledClass);
19999 e.dom.disabled = true;
20003 this.disabled = true;
20004 this.fireEvent("disable", this);
20008 enable : function()
20010 if(this.inputType != 'radio'){
20011 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20018 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20019 _this.getActionEl().removeClass(this.disabledClass);
20020 e.dom.disabled = false;
20024 this.disabled = false;
20025 this.fireEvent("enable", this);
20031 Roo.apply(Roo.bootstrap.CheckBox, {
20036 * register a CheckBox Group
20037 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20039 register : function(checkbox)
20041 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20042 this.groups[checkbox.groupId] = {};
20045 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20049 this.groups[checkbox.groupId][checkbox.name] = checkbox;
20053 * fetch a CheckBox Group based on the group ID
20054 * @param {string} the group ID
20055 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20057 get: function(groupId) {
20058 if (typeof(this.groups[groupId]) == 'undefined') {
20062 return this.groups[groupId] ;
20074 *<div class="radio">
20076 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
20077 Option one is this and that—be sure to include why it's great
20084 *<label class="radio-inline">fieldLabel</label>
20085 *<label class="radio-inline">
20086 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
20094 * @class Roo.bootstrap.Radio
20095 * @extends Roo.bootstrap.CheckBox
20096 * Bootstrap Radio class
20099 * Create a new Radio
20100 * @param {Object} config The config object
20103 Roo.bootstrap.Radio = function(config){
20104 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20108 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
20110 inputType: 'radio',
20114 getAutoCreate : function()
20116 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20117 align = align || 'left'; // default...
20124 tag : this.inline ? 'span' : 'div',
20125 cls : 'form-group',
20129 var inline = this.inline ? ' radio-inline' : '';
20133 // does not need for, as we wrap the input with it..
20135 cls : 'control-label box-label' + inline,
20138 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
20142 //cls : 'control-label' + inline,
20143 html : this.fieldLabel,
20144 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
20150 type : this.inputType,
20151 //value : (!this.checked) ? this.valueOff : this.inputValue,
20152 value : this.inputValue,
20154 placeholder : this.placeholder || '' // ?? needed????
20157 if (this.weight) { // Validity check?
20158 input.cls += " radio-" + this.weight;
20160 if (this.disabled) {
20161 input.disabled=true;
20165 input.checked = this.checked;
20169 input.name = this.name;
20173 input.cls += ' input-' + this.size;
20176 //?? can span's inline have a width??
20179 ['xs','sm','md','lg'].map(function(size){
20180 if (settings[size]) {
20181 cfg.cls += ' col-' + size + '-' + settings[size];
20185 var inputblock = input;
20187 if (this.before || this.after) {
20190 cls : 'input-group',
20195 inputblock.cn.push({
20197 cls : 'input-group-addon',
20201 inputblock.cn.push(input);
20203 inputblock.cn.push({
20205 cls : 'input-group-addon',
20213 if (this.fieldLabel && this.fieldLabel.length) {
20214 cfg.cn.push(fieldLabel);
20217 // normal bootstrap puts the input inside the label.
20218 // however with our styled version - it has to go after the input.
20220 //lbl.cn.push(inputblock);
20224 cls: 'radio' + inline,
20231 cfg.cn.push( lblwrap);
20236 html: this.boxLabel
20245 initEvents : function()
20247 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20249 this.inputEl().on('click', this.onClick, this);
20250 if (this.boxLabel) {
20251 //Roo.log('find label');
20252 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
20257 inputEl: function ()
20259 return this.el.select('input.roo-radio',true).first();
20261 onClick : function()
20264 this.setChecked(true);
20267 setChecked : function(state,suppressEvent)
20270 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20271 v.dom.checked = false;
20274 Roo.log(this.inputEl().dom);
20275 this.checked = state;
20276 this.inputEl().dom.checked = state;
20278 if(suppressEvent !== true){
20279 this.fireEvent('check', this, state);
20282 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20286 getGroupValue : function()
20289 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20290 if(v.dom.checked == true){
20291 value = v.dom.value;
20299 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
20300 * @return {Mixed} value The field value
20302 getValue : function(){
20303 return this.getGroupValue();
20307 //<script type="text/javascript">
20310 * Based Ext JS Library 1.1.1
20311 * Copyright(c) 2006-2007, Ext JS, LLC.
20317 * @class Roo.HtmlEditorCore
20318 * @extends Roo.Component
20319 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20321 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20324 Roo.HtmlEditorCore = function(config){
20327 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20332 * @event initialize
20333 * Fires when the editor is fully initialized (including the iframe)
20334 * @param {Roo.HtmlEditorCore} this
20339 * Fires when the editor is first receives the focus. Any insertion must wait
20340 * until after this event.
20341 * @param {Roo.HtmlEditorCore} this
20345 * @event beforesync
20346 * Fires before the textarea is updated with content from the editor iframe. Return false
20347 * to cancel the sync.
20348 * @param {Roo.HtmlEditorCore} this
20349 * @param {String} html
20353 * @event beforepush
20354 * Fires before the iframe editor is updated with content from the textarea. Return false
20355 * to cancel the push.
20356 * @param {Roo.HtmlEditorCore} this
20357 * @param {String} html
20362 * Fires when the textarea is updated with content from the editor iframe.
20363 * @param {Roo.HtmlEditorCore} this
20364 * @param {String} html
20369 * Fires when the iframe editor is updated with content from the textarea.
20370 * @param {Roo.HtmlEditorCore} this
20371 * @param {String} html
20376 * @event editorevent
20377 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20378 * @param {Roo.HtmlEditorCore} this
20384 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20386 // defaults : white / black...
20387 this.applyBlacklists();
20394 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20398 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20404 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20409 * @cfg {Number} height (in pixels)
20413 * @cfg {Number} width (in pixels)
20418 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20421 stylesheets: false,
20426 // private properties
20427 validationEvent : false,
20429 initialized : false,
20431 sourceEditMode : false,
20432 onFocus : Roo.emptyFn,
20434 hideMode:'offsets',
20438 // blacklist + whitelisted elements..
20445 * Protected method that will not generally be called directly. It
20446 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20447 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20449 getDocMarkup : function(){
20453 // inherit styels from page...??
20454 if (this.stylesheets === false) {
20456 Roo.get(document.head).select('style').each(function(node) {
20457 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20460 Roo.get(document.head).select('link').each(function(node) {
20461 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20464 } else if (!this.stylesheets.length) {
20466 st = '<style type="text/css">' +
20467 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20473 st += '<style type="text/css">' +
20474 'IMG { cursor: pointer } ' +
20478 return '<html><head>' + st +
20479 //<style type="text/css">' +
20480 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20482 ' </head><body class="roo-htmleditor-body"></body></html>';
20486 onRender : function(ct, position)
20489 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20490 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20493 this.el.dom.style.border = '0 none';
20494 this.el.dom.setAttribute('tabIndex', -1);
20495 this.el.addClass('x-hidden hide');
20499 if(Roo.isIE){ // fix IE 1px bogus margin
20500 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20504 this.frameId = Roo.id();
20508 var iframe = this.owner.wrap.createChild({
20510 cls: 'form-control', // bootstrap..
20512 name: this.frameId,
20513 frameBorder : 'no',
20514 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20519 this.iframe = iframe.dom;
20521 this.assignDocWin();
20523 this.doc.designMode = 'on';
20526 this.doc.write(this.getDocMarkup());
20530 var task = { // must defer to wait for browser to be ready
20532 //console.log("run task?" + this.doc.readyState);
20533 this.assignDocWin();
20534 if(this.doc.body || this.doc.readyState == 'complete'){
20536 this.doc.designMode="on";
20540 Roo.TaskMgr.stop(task);
20541 this.initEditor.defer(10, this);
20548 Roo.TaskMgr.start(task);
20553 onResize : function(w, h)
20555 Roo.log('resize: ' +w + ',' + h );
20556 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20560 if(typeof w == 'number'){
20562 this.iframe.style.width = w + 'px';
20564 if(typeof h == 'number'){
20566 this.iframe.style.height = h + 'px';
20568 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20575 * Toggles the editor between standard and source edit mode.
20576 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20578 toggleSourceEdit : function(sourceEditMode){
20580 this.sourceEditMode = sourceEditMode === true;
20582 if(this.sourceEditMode){
20584 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20587 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20588 //this.iframe.className = '';
20591 //this.setSize(this.owner.wrap.getSize());
20592 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20599 * Protected method that will not generally be called directly. If you need/want
20600 * custom HTML cleanup, this is the method you should override.
20601 * @param {String} html The HTML to be cleaned
20602 * return {String} The cleaned HTML
20604 cleanHtml : function(html){
20605 html = String(html);
20606 if(html.length > 5){
20607 if(Roo.isSafari){ // strip safari nonsense
20608 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20611 if(html == ' '){
20618 * HTML Editor -> Textarea
20619 * Protected method that will not generally be called directly. Syncs the contents
20620 * of the editor iframe with the textarea.
20622 syncValue : function(){
20623 if(this.initialized){
20624 var bd = (this.doc.body || this.doc.documentElement);
20625 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20626 var html = bd.innerHTML;
20628 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20629 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20631 html = '<div style="'+m[0]+'">' + html + '</div>';
20634 html = this.cleanHtml(html);
20635 // fix up the special chars.. normaly like back quotes in word...
20636 // however we do not want to do this with chinese..
20637 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20638 var cc = b.charCodeAt();
20640 (cc >= 0x4E00 && cc < 0xA000 ) ||
20641 (cc >= 0x3400 && cc < 0x4E00 ) ||
20642 (cc >= 0xf900 && cc < 0xfb00 )
20648 if(this.owner.fireEvent('beforesync', this, html) !== false){
20649 this.el.dom.value = html;
20650 this.owner.fireEvent('sync', this, html);
20656 * Protected method that will not generally be called directly. Pushes the value of the textarea
20657 * into the iframe editor.
20659 pushValue : function(){
20660 if(this.initialized){
20661 var v = this.el.dom.value.trim();
20663 // if(v.length < 1){
20667 if(this.owner.fireEvent('beforepush', this, v) !== false){
20668 var d = (this.doc.body || this.doc.documentElement);
20670 this.cleanUpPaste();
20671 this.el.dom.value = d.innerHTML;
20672 this.owner.fireEvent('push', this, v);
20678 deferFocus : function(){
20679 this.focus.defer(10, this);
20683 focus : function(){
20684 if(this.win && !this.sourceEditMode){
20691 assignDocWin: function()
20693 var iframe = this.iframe;
20696 this.doc = iframe.contentWindow.document;
20697 this.win = iframe.contentWindow;
20699 // if (!Roo.get(this.frameId)) {
20702 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20703 // this.win = Roo.get(this.frameId).dom.contentWindow;
20705 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20709 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20710 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20715 initEditor : function(){
20716 //console.log("INIT EDITOR");
20717 this.assignDocWin();
20721 this.doc.designMode="on";
20723 this.doc.write(this.getDocMarkup());
20726 var dbody = (this.doc.body || this.doc.documentElement);
20727 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20728 // this copies styles from the containing element into thsi one..
20729 // not sure why we need all of this..
20730 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20732 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20733 //ss['background-attachment'] = 'fixed'; // w3c
20734 dbody.bgProperties = 'fixed'; // ie
20735 //Roo.DomHelper.applyStyles(dbody, ss);
20736 Roo.EventManager.on(this.doc, {
20737 //'mousedown': this.onEditorEvent,
20738 'mouseup': this.onEditorEvent,
20739 'dblclick': this.onEditorEvent,
20740 'click': this.onEditorEvent,
20741 'keyup': this.onEditorEvent,
20746 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20748 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20749 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20751 this.initialized = true;
20753 this.owner.fireEvent('initialize', this);
20758 onDestroy : function(){
20764 //for (var i =0; i < this.toolbars.length;i++) {
20765 // // fixme - ask toolbars for heights?
20766 // this.toolbars[i].onDestroy();
20769 //this.wrap.dom.innerHTML = '';
20770 //this.wrap.remove();
20775 onFirstFocus : function(){
20777 this.assignDocWin();
20780 this.activated = true;
20783 if(Roo.isGecko){ // prevent silly gecko errors
20785 var s = this.win.getSelection();
20786 if(!s.focusNode || s.focusNode.nodeType != 3){
20787 var r = s.getRangeAt(0);
20788 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20793 this.execCmd('useCSS', true);
20794 this.execCmd('styleWithCSS', false);
20797 this.owner.fireEvent('activate', this);
20801 adjustFont: function(btn){
20802 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20803 //if(Roo.isSafari){ // safari
20806 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20807 if(Roo.isSafari){ // safari
20808 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20809 v = (v < 10) ? 10 : v;
20810 v = (v > 48) ? 48 : v;
20811 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20816 v = Math.max(1, v+adjust);
20818 this.execCmd('FontSize', v );
20821 onEditorEvent : function(e)
20823 this.owner.fireEvent('editorevent', this, e);
20824 // this.updateToolbar();
20825 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20828 insertTag : function(tg)
20830 // could be a bit smarter... -> wrap the current selected tRoo..
20831 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20833 range = this.createRange(this.getSelection());
20834 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20835 wrappingNode.appendChild(range.extractContents());
20836 range.insertNode(wrappingNode);
20843 this.execCmd("formatblock", tg);
20847 insertText : function(txt)
20851 var range = this.createRange();
20852 range.deleteContents();
20853 //alert(Sender.getAttribute('label'));
20855 range.insertNode(this.doc.createTextNode(txt));
20861 * Executes a Midas editor command on the editor document and performs necessary focus and
20862 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20863 * @param {String} cmd The Midas command
20864 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20866 relayCmd : function(cmd, value){
20868 this.execCmd(cmd, value);
20869 this.owner.fireEvent('editorevent', this);
20870 //this.updateToolbar();
20871 this.owner.deferFocus();
20875 * Executes a Midas editor command directly on the editor document.
20876 * For visual commands, you should use {@link #relayCmd} instead.
20877 * <b>This should only be called after the editor is initialized.</b>
20878 * @param {String} cmd The Midas command
20879 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20881 execCmd : function(cmd, value){
20882 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20889 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20891 * @param {String} text | dom node..
20893 insertAtCursor : function(text)
20898 if(!this.activated){
20904 var r = this.doc.selection.createRange();
20915 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20919 // from jquery ui (MIT licenced)
20921 var win = this.win;
20923 if (win.getSelection && win.getSelection().getRangeAt) {
20924 range = win.getSelection().getRangeAt(0);
20925 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20926 range.insertNode(node);
20927 } else if (win.document.selection && win.document.selection.createRange) {
20928 // no firefox support
20929 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20930 win.document.selection.createRange().pasteHTML(txt);
20932 // no firefox support
20933 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20934 this.execCmd('InsertHTML', txt);
20943 mozKeyPress : function(e){
20945 var c = e.getCharCode(), cmd;
20948 c = String.fromCharCode(c).toLowerCase();
20962 this.cleanUpPaste.defer(100, this);
20970 e.preventDefault();
20978 fixKeys : function(){ // load time branching for fastest keydown performance
20980 return function(e){
20981 var k = e.getKey(), r;
20984 r = this.doc.selection.createRange();
20987 r.pasteHTML('    ');
20994 r = this.doc.selection.createRange();
20996 var target = r.parentElement();
20997 if(!target || target.tagName.toLowerCase() != 'li'){
20999 r.pasteHTML('<br />');
21005 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21006 this.cleanUpPaste.defer(100, this);
21012 }else if(Roo.isOpera){
21013 return function(e){
21014 var k = e.getKey();
21018 this.execCmd('InsertHTML','    ');
21021 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21022 this.cleanUpPaste.defer(100, this);
21027 }else if(Roo.isSafari){
21028 return function(e){
21029 var k = e.getKey();
21033 this.execCmd('InsertText','\t');
21037 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21038 this.cleanUpPaste.defer(100, this);
21046 getAllAncestors: function()
21048 var p = this.getSelectedNode();
21051 a.push(p); // push blank onto stack..
21052 p = this.getParentElement();
21056 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21060 a.push(this.doc.body);
21064 lastSelNode : false,
21067 getSelection : function()
21069 this.assignDocWin();
21070 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21073 getSelectedNode: function()
21075 // this may only work on Gecko!!!
21077 // should we cache this!!!!
21082 var range = this.createRange(this.getSelection()).cloneRange();
21085 var parent = range.parentElement();
21087 var testRange = range.duplicate();
21088 testRange.moveToElementText(parent);
21089 if (testRange.inRange(range)) {
21092 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21095 parent = parent.parentElement;
21100 // is ancestor a text element.
21101 var ac = range.commonAncestorContainer;
21102 if (ac.nodeType == 3) {
21103 ac = ac.parentNode;
21106 var ar = ac.childNodes;
21109 var other_nodes = [];
21110 var has_other_nodes = false;
21111 for (var i=0;i<ar.length;i++) {
21112 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21115 // fullly contained node.
21117 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21122 // probably selected..
21123 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21124 other_nodes.push(ar[i]);
21128 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21133 has_other_nodes = true;
21135 if (!nodes.length && other_nodes.length) {
21136 nodes= other_nodes;
21138 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21144 createRange: function(sel)
21146 // this has strange effects when using with
21147 // top toolbar - not sure if it's a great idea.
21148 //this.editor.contentWindow.focus();
21149 if (typeof sel != "undefined") {
21151 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21153 return this.doc.createRange();
21156 return this.doc.createRange();
21159 getParentElement: function()
21162 this.assignDocWin();
21163 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21165 var range = this.createRange(sel);
21168 var p = range.commonAncestorContainer;
21169 while (p.nodeType == 3) { // text node
21180 * Range intersection.. the hard stuff...
21184 * [ -- selected range --- ]
21188 * if end is before start or hits it. fail.
21189 * if start is after end or hits it fail.
21191 * if either hits (but other is outside. - then it's not
21197 // @see http://www.thismuchiknow.co.uk/?p=64.
21198 rangeIntersectsNode : function(range, node)
21200 var nodeRange = node.ownerDocument.createRange();
21202 nodeRange.selectNode(node);
21204 nodeRange.selectNodeContents(node);
21207 var rangeStartRange = range.cloneRange();
21208 rangeStartRange.collapse(true);
21210 var rangeEndRange = range.cloneRange();
21211 rangeEndRange.collapse(false);
21213 var nodeStartRange = nodeRange.cloneRange();
21214 nodeStartRange.collapse(true);
21216 var nodeEndRange = nodeRange.cloneRange();
21217 nodeEndRange.collapse(false);
21219 return rangeStartRange.compareBoundaryPoints(
21220 Range.START_TO_START, nodeEndRange) == -1 &&
21221 rangeEndRange.compareBoundaryPoints(
21222 Range.START_TO_START, nodeStartRange) == 1;
21226 rangeCompareNode : function(range, node)
21228 var nodeRange = node.ownerDocument.createRange();
21230 nodeRange.selectNode(node);
21232 nodeRange.selectNodeContents(node);
21236 range.collapse(true);
21238 nodeRange.collapse(true);
21240 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21241 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
21243 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21245 var nodeIsBefore = ss == 1;
21246 var nodeIsAfter = ee == -1;
21248 if (nodeIsBefore && nodeIsAfter) {
21251 if (!nodeIsBefore && nodeIsAfter) {
21252 return 1; //right trailed.
21255 if (nodeIsBefore && !nodeIsAfter) {
21256 return 2; // left trailed.
21262 // private? - in a new class?
21263 cleanUpPaste : function()
21265 // cleans up the whole document..
21266 Roo.log('cleanuppaste');
21268 this.cleanUpChildren(this.doc.body);
21269 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21270 if (clean != this.doc.body.innerHTML) {
21271 this.doc.body.innerHTML = clean;
21276 cleanWordChars : function(input) {// change the chars to hex code
21277 var he = Roo.HtmlEditorCore;
21279 var output = input;
21280 Roo.each(he.swapCodes, function(sw) {
21281 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21283 output = output.replace(swapper, sw[1]);
21290 cleanUpChildren : function (n)
21292 if (!n.childNodes.length) {
21295 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21296 this.cleanUpChild(n.childNodes[i]);
21303 cleanUpChild : function (node)
21306 //console.log(node);
21307 if (node.nodeName == "#text") {
21308 // clean up silly Windows -- stuff?
21311 if (node.nodeName == "#comment") {
21312 node.parentNode.removeChild(node);
21313 // clean up silly Windows -- stuff?
21316 var lcname = node.tagName.toLowerCase();
21317 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21318 // whitelist of tags..
21320 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21322 node.parentNode.removeChild(node);
21327 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21329 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21330 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21332 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21333 // remove_keep_children = true;
21336 if (remove_keep_children) {
21337 this.cleanUpChildren(node);
21338 // inserts everything just before this node...
21339 while (node.childNodes.length) {
21340 var cn = node.childNodes[0];
21341 node.removeChild(cn);
21342 node.parentNode.insertBefore(cn, node);
21344 node.parentNode.removeChild(node);
21348 if (!node.attributes || !node.attributes.length) {
21349 this.cleanUpChildren(node);
21353 function cleanAttr(n,v)
21356 if (v.match(/^\./) || v.match(/^\//)) {
21359 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21362 if (v.match(/^#/)) {
21365 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21366 node.removeAttribute(n);
21370 var cwhite = this.cwhite;
21371 var cblack = this.cblack;
21373 function cleanStyle(n,v)
21375 if (v.match(/expression/)) { //XSS?? should we even bother..
21376 node.removeAttribute(n);
21380 var parts = v.split(/;/);
21383 Roo.each(parts, function(p) {
21384 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21388 var l = p.split(':').shift().replace(/\s+/g,'');
21389 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21391 if ( cwhite.length && cblack.indexOf(l) > -1) {
21392 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21393 //node.removeAttribute(n);
21397 // only allow 'c whitelisted system attributes'
21398 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21399 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21400 //node.removeAttribute(n);
21410 if (clean.length) {
21411 node.setAttribute(n, clean.join(';'));
21413 node.removeAttribute(n);
21419 for (var i = node.attributes.length-1; i > -1 ; i--) {
21420 var a = node.attributes[i];
21423 if (a.name.toLowerCase().substr(0,2)=='on') {
21424 node.removeAttribute(a.name);
21427 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21428 node.removeAttribute(a.name);
21431 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21432 cleanAttr(a.name,a.value); // fixme..
21435 if (a.name == 'style') {
21436 cleanStyle(a.name,a.value);
21439 /// clean up MS crap..
21440 // tecnically this should be a list of valid class'es..
21443 if (a.name == 'class') {
21444 if (a.value.match(/^Mso/)) {
21445 node.className = '';
21448 if (a.value.match(/body/)) {
21449 node.className = '';
21460 this.cleanUpChildren(node);
21466 * Clean up MS wordisms...
21468 cleanWord : function(node)
21473 this.cleanWord(this.doc.body);
21476 if (node.nodeName == "#text") {
21477 // clean up silly Windows -- stuff?
21480 if (node.nodeName == "#comment") {
21481 node.parentNode.removeChild(node);
21482 // clean up silly Windows -- stuff?
21486 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21487 node.parentNode.removeChild(node);
21491 // remove - but keep children..
21492 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21493 while (node.childNodes.length) {
21494 var cn = node.childNodes[0];
21495 node.removeChild(cn);
21496 node.parentNode.insertBefore(cn, node);
21498 node.parentNode.removeChild(node);
21499 this.iterateChildren(node, this.cleanWord);
21503 if (node.className.length) {
21505 var cn = node.className.split(/\W+/);
21507 Roo.each(cn, function(cls) {
21508 if (cls.match(/Mso[a-zA-Z]+/)) {
21513 node.className = cna.length ? cna.join(' ') : '';
21515 node.removeAttribute("class");
21519 if (node.hasAttribute("lang")) {
21520 node.removeAttribute("lang");
21523 if (node.hasAttribute("style")) {
21525 var styles = node.getAttribute("style").split(";");
21527 Roo.each(styles, function(s) {
21528 if (!s.match(/:/)) {
21531 var kv = s.split(":");
21532 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21535 // what ever is left... we allow.
21538 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21539 if (!nstyle.length) {
21540 node.removeAttribute('style');
21543 this.iterateChildren(node, this.cleanWord);
21549 * iterateChildren of a Node, calling fn each time, using this as the scole..
21550 * @param {DomNode} node node to iterate children of.
21551 * @param {Function} fn method of this class to call on each item.
21553 iterateChildren : function(node, fn)
21555 if (!node.childNodes.length) {
21558 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21559 fn.call(this, node.childNodes[i])
21565 * cleanTableWidths.
21567 * Quite often pasting from word etc.. results in tables with column and widths.
21568 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21571 cleanTableWidths : function(node)
21576 this.cleanTableWidths(this.doc.body);
21581 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21584 Roo.log(node.tagName);
21585 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21586 this.iterateChildren(node, this.cleanTableWidths);
21589 if (node.hasAttribute('width')) {
21590 node.removeAttribute('width');
21594 if (node.hasAttribute("style")) {
21597 var styles = node.getAttribute("style").split(";");
21599 Roo.each(styles, function(s) {
21600 if (!s.match(/:/)) {
21603 var kv = s.split(":");
21604 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21607 // what ever is left... we allow.
21610 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21611 if (!nstyle.length) {
21612 node.removeAttribute('style');
21616 this.iterateChildren(node, this.cleanTableWidths);
21624 domToHTML : function(currentElement, depth, nopadtext) {
21626 depth = depth || 0;
21627 nopadtext = nopadtext || false;
21629 if (!currentElement) {
21630 return this.domToHTML(this.doc.body);
21633 //Roo.log(currentElement);
21635 var allText = false;
21636 var nodeName = currentElement.nodeName;
21637 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21639 if (nodeName == '#text') {
21641 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21646 if (nodeName != 'BODY') {
21649 // Prints the node tagName, such as <A>, <IMG>, etc
21652 for(i = 0; i < currentElement.attributes.length;i++) {
21654 var aname = currentElement.attributes.item(i).name;
21655 if (!currentElement.attributes.item(i).value.length) {
21658 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21661 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21670 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21673 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21678 // Traverse the tree
21680 var currentElementChild = currentElement.childNodes.item(i);
21681 var allText = true;
21682 var innerHTML = '';
21684 while (currentElementChild) {
21685 // Formatting code (indent the tree so it looks nice on the screen)
21686 var nopad = nopadtext;
21687 if (lastnode == 'SPAN') {
21691 if (currentElementChild.nodeName == '#text') {
21692 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21693 toadd = nopadtext ? toadd : toadd.trim();
21694 if (!nopad && toadd.length > 80) {
21695 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21697 innerHTML += toadd;
21700 currentElementChild = currentElement.childNodes.item(i);
21706 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21708 // Recursively traverse the tree structure of the child node
21709 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21710 lastnode = currentElementChild.nodeName;
21712 currentElementChild=currentElement.childNodes.item(i);
21718 // The remaining code is mostly for formatting the tree
21719 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21724 ret+= "</"+tagName+">";
21730 applyBlacklists : function()
21732 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21733 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21737 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21738 if (b.indexOf(tag) > -1) {
21741 this.white.push(tag);
21745 Roo.each(w, function(tag) {
21746 if (b.indexOf(tag) > -1) {
21749 if (this.white.indexOf(tag) > -1) {
21752 this.white.push(tag);
21757 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21758 if (w.indexOf(tag) > -1) {
21761 this.black.push(tag);
21765 Roo.each(b, function(tag) {
21766 if (w.indexOf(tag) > -1) {
21769 if (this.black.indexOf(tag) > -1) {
21772 this.black.push(tag);
21777 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21778 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21782 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21783 if (b.indexOf(tag) > -1) {
21786 this.cwhite.push(tag);
21790 Roo.each(w, function(tag) {
21791 if (b.indexOf(tag) > -1) {
21794 if (this.cwhite.indexOf(tag) > -1) {
21797 this.cwhite.push(tag);
21802 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21803 if (w.indexOf(tag) > -1) {
21806 this.cblack.push(tag);
21810 Roo.each(b, function(tag) {
21811 if (w.indexOf(tag) > -1) {
21814 if (this.cblack.indexOf(tag) > -1) {
21817 this.cblack.push(tag);
21822 setStylesheets : function(stylesheets)
21824 if(typeof(stylesheets) == 'string'){
21825 Roo.get(this.iframe.contentDocument.head).createChild({
21827 rel : 'stylesheet',
21836 Roo.each(stylesheets, function(s) {
21841 Roo.get(_this.iframe.contentDocument.head).createChild({
21843 rel : 'stylesheet',
21852 removeStylesheets : function()
21856 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21861 // hide stuff that is not compatible
21875 * @event specialkey
21879 * @cfg {String} fieldClass @hide
21882 * @cfg {String} focusClass @hide
21885 * @cfg {String} autoCreate @hide
21888 * @cfg {String} inputType @hide
21891 * @cfg {String} invalidClass @hide
21894 * @cfg {String} invalidText @hide
21897 * @cfg {String} msgFx @hide
21900 * @cfg {String} validateOnBlur @hide
21904 Roo.HtmlEditorCore.white = [
21905 'area', 'br', 'img', 'input', 'hr', 'wbr',
21907 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21908 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21909 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21910 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21911 'table', 'ul', 'xmp',
21913 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21916 'dir', 'menu', 'ol', 'ul', 'dl',
21922 Roo.HtmlEditorCore.black = [
21923 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21925 'base', 'basefont', 'bgsound', 'blink', 'body',
21926 'frame', 'frameset', 'head', 'html', 'ilayer',
21927 'iframe', 'layer', 'link', 'meta', 'object',
21928 'script', 'style' ,'title', 'xml' // clean later..
21930 Roo.HtmlEditorCore.clean = [
21931 'script', 'style', 'title', 'xml'
21933 Roo.HtmlEditorCore.remove = [
21938 Roo.HtmlEditorCore.ablack = [
21942 Roo.HtmlEditorCore.aclean = [
21943 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21947 Roo.HtmlEditorCore.pwhite= [
21948 'http', 'https', 'mailto'
21951 // white listed style attributes.
21952 Roo.HtmlEditorCore.cwhite= [
21953 // 'text-align', /// default is to allow most things..
21959 // black listed style attributes.
21960 Roo.HtmlEditorCore.cblack= [
21961 // 'font-size' -- this can be set by the project
21965 Roo.HtmlEditorCore.swapCodes =[
21984 * @class Roo.bootstrap.HtmlEditor
21985 * @extends Roo.bootstrap.TextArea
21986 * Bootstrap HtmlEditor class
21989 * Create a new HtmlEditor
21990 * @param {Object} config The config object
21993 Roo.bootstrap.HtmlEditor = function(config){
21994 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21995 if (!this.toolbars) {
21996 this.toolbars = [];
21998 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22001 * @event initialize
22002 * Fires when the editor is fully initialized (including the iframe)
22003 * @param {HtmlEditor} this
22008 * Fires when the editor is first receives the focus. Any insertion must wait
22009 * until after this event.
22010 * @param {HtmlEditor} this
22014 * @event beforesync
22015 * Fires before the textarea is updated with content from the editor iframe. Return false
22016 * to cancel the sync.
22017 * @param {HtmlEditor} this
22018 * @param {String} html
22022 * @event beforepush
22023 * Fires before the iframe editor is updated with content from the textarea. Return false
22024 * to cancel the push.
22025 * @param {HtmlEditor} this
22026 * @param {String} html
22031 * Fires when the textarea is updated with content from the editor iframe.
22032 * @param {HtmlEditor} this
22033 * @param {String} html
22038 * Fires when the iframe editor is updated with content from the textarea.
22039 * @param {HtmlEditor} this
22040 * @param {String} html
22044 * @event editmodechange
22045 * Fires when the editor switches edit modes
22046 * @param {HtmlEditor} this
22047 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22049 editmodechange: true,
22051 * @event editorevent
22052 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22053 * @param {HtmlEditor} this
22057 * @event firstfocus
22058 * Fires when on first focus - needed by toolbars..
22059 * @param {HtmlEditor} this
22064 * Auto save the htmlEditor value as a file into Events
22065 * @param {HtmlEditor} this
22069 * @event savedpreview
22070 * preview the saved version of htmlEditor
22071 * @param {HtmlEditor} this
22078 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
22082 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22087 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22092 * @cfg {Number} height (in pixels)
22096 * @cfg {Number} width (in pixels)
22101 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22104 stylesheets: false,
22109 // private properties
22110 validationEvent : false,
22112 initialized : false,
22115 onFocus : Roo.emptyFn,
22117 hideMode:'offsets',
22120 tbContainer : false,
22122 toolbarContainer :function() {
22123 return this.wrap.select('.x-html-editor-tb',true).first();
22127 * Protected method that will not generally be called directly. It
22128 * is called when the editor creates its toolbar. Override this method if you need to
22129 * add custom toolbar buttons.
22130 * @param {HtmlEditor} editor
22132 createToolbar : function(){
22134 Roo.log("create toolbars");
22136 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22137 this.toolbars[0].render(this.toolbarContainer());
22141 // if (!editor.toolbars || !editor.toolbars.length) {
22142 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22145 // for (var i =0 ; i < editor.toolbars.length;i++) {
22146 // editor.toolbars[i] = Roo.factory(
22147 // typeof(editor.toolbars[i]) == 'string' ?
22148 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
22149 // Roo.bootstrap.HtmlEditor);
22150 // editor.toolbars[i].init(editor);
22156 onRender : function(ct, position)
22158 // Roo.log("Call onRender: " + this.xtype);
22160 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22162 this.wrap = this.inputEl().wrap({
22163 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22166 this.editorcore.onRender(ct, position);
22168 if (this.resizable) {
22169 this.resizeEl = new Roo.Resizable(this.wrap, {
22173 minHeight : this.height,
22174 height: this.height,
22175 handles : this.resizable,
22178 resize : function(r, w, h) {
22179 _t.onResize(w,h); // -something
22185 this.createToolbar(this);
22188 if(!this.width && this.resizable){
22189 this.setSize(this.wrap.getSize());
22191 if (this.resizeEl) {
22192 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22193 // should trigger onReize..
22199 onResize : function(w, h)
22201 Roo.log('resize: ' +w + ',' + h );
22202 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22206 if(this.inputEl() ){
22207 if(typeof w == 'number'){
22208 var aw = w - this.wrap.getFrameWidth('lr');
22209 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22212 if(typeof h == 'number'){
22213 var tbh = -11; // fixme it needs to tool bar size!
22214 for (var i =0; i < this.toolbars.length;i++) {
22215 // fixme - ask toolbars for heights?
22216 tbh += this.toolbars[i].el.getHeight();
22217 //if (this.toolbars[i].footer) {
22218 // tbh += this.toolbars[i].footer.el.getHeight();
22226 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22227 ah -= 5; // knock a few pixes off for look..
22228 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22232 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22233 this.editorcore.onResize(ew,eh);
22238 * Toggles the editor between standard and source edit mode.
22239 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22241 toggleSourceEdit : function(sourceEditMode)
22243 this.editorcore.toggleSourceEdit(sourceEditMode);
22245 if(this.editorcore.sourceEditMode){
22246 Roo.log('editor - showing textarea');
22249 // Roo.log(this.syncValue());
22251 this.inputEl().removeClass(['hide', 'x-hidden']);
22252 this.inputEl().dom.removeAttribute('tabIndex');
22253 this.inputEl().focus();
22255 Roo.log('editor - hiding textarea');
22257 // Roo.log(this.pushValue());
22260 this.inputEl().addClass(['hide', 'x-hidden']);
22261 this.inputEl().dom.setAttribute('tabIndex', -1);
22262 //this.deferFocus();
22265 if(this.resizable){
22266 this.setSize(this.wrap.getSize());
22269 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22272 // private (for BoxComponent)
22273 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22275 // private (for BoxComponent)
22276 getResizeEl : function(){
22280 // private (for BoxComponent)
22281 getPositionEl : function(){
22286 initEvents : function(){
22287 this.originalValue = this.getValue();
22291 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22294 // markInvalid : Roo.emptyFn,
22296 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22299 // clearInvalid : Roo.emptyFn,
22301 setValue : function(v){
22302 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22303 this.editorcore.pushValue();
22308 deferFocus : function(){
22309 this.focus.defer(10, this);
22313 focus : function(){
22314 this.editorcore.focus();
22320 onDestroy : function(){
22326 for (var i =0; i < this.toolbars.length;i++) {
22327 // fixme - ask toolbars for heights?
22328 this.toolbars[i].onDestroy();
22331 this.wrap.dom.innerHTML = '';
22332 this.wrap.remove();
22337 onFirstFocus : function(){
22338 //Roo.log("onFirstFocus");
22339 this.editorcore.onFirstFocus();
22340 for (var i =0; i < this.toolbars.length;i++) {
22341 this.toolbars[i].onFirstFocus();
22347 syncValue : function()
22349 this.editorcore.syncValue();
22352 pushValue : function()
22354 this.editorcore.pushValue();
22358 // hide stuff that is not compatible
22372 * @event specialkey
22376 * @cfg {String} fieldClass @hide
22379 * @cfg {String} focusClass @hide
22382 * @cfg {String} autoCreate @hide
22385 * @cfg {String} inputType @hide
22388 * @cfg {String} invalidClass @hide
22391 * @cfg {String} invalidText @hide
22394 * @cfg {String} msgFx @hide
22397 * @cfg {String} validateOnBlur @hide
22406 Roo.namespace('Roo.bootstrap.htmleditor');
22408 * @class Roo.bootstrap.HtmlEditorToolbar1
22413 new Roo.bootstrap.HtmlEditor({
22416 new Roo.bootstrap.HtmlEditorToolbar1({
22417 disable : { fonts: 1 , format: 1, ..., ... , ...],
22423 * @cfg {Object} disable List of elements to disable..
22424 * @cfg {Array} btns List of additional buttons.
22428 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22431 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22434 Roo.apply(this, config);
22436 // default disabled, based on 'good practice'..
22437 this.disable = this.disable || {};
22438 Roo.applyIf(this.disable, {
22441 specialElements : true
22443 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22445 this.editor = config.editor;
22446 this.editorcore = config.editor.editorcore;
22448 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22450 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22451 // dont call parent... till later.
22453 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
22458 editorcore : false,
22463 "h1","h2","h3","h4","h5","h6",
22465 "abbr", "acronym", "address", "cite", "samp", "var",
22469 onRender : function(ct, position)
22471 // Roo.log("Call onRender: " + this.xtype);
22473 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22475 this.el.dom.style.marginBottom = '0';
22477 var editorcore = this.editorcore;
22478 var editor= this.editor;
22481 var btn = function(id,cmd , toggle, handler){
22483 var event = toggle ? 'toggle' : 'click';
22488 xns: Roo.bootstrap,
22491 enableToggle:toggle !== false,
22493 pressed : toggle ? false : null,
22496 a.listeners[toggle ? 'toggle' : 'click'] = function() {
22497 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
22506 xns: Roo.bootstrap,
22507 glyphicon : 'font',
22511 xns: Roo.bootstrap,
22515 Roo.each(this.formats, function(f) {
22516 style.menu.items.push({
22518 xns: Roo.bootstrap,
22519 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22524 editorcore.insertTag(this.tagname);
22531 children.push(style);
22534 btn('bold',false,true);
22535 btn('italic',false,true);
22536 btn('align-left', 'justifyleft',true);
22537 btn('align-center', 'justifycenter',true);
22538 btn('align-right' , 'justifyright',true);
22539 btn('link', false, false, function(btn) {
22540 //Roo.log("create link?");
22541 var url = prompt(this.createLinkText, this.defaultLinkValue);
22542 if(url && url != 'http:/'+'/'){
22543 this.editorcore.relayCmd('createlink', url);
22546 btn('list','insertunorderedlist',true);
22547 btn('pencil', false,true, function(btn){
22550 this.toggleSourceEdit(btn.pressed);
22556 xns: Roo.bootstrap,
22561 xns: Roo.bootstrap,
22566 cog.menu.items.push({
22568 xns: Roo.bootstrap,
22569 html : Clean styles,
22574 editorcore.insertTag(this.tagname);
22583 this.xtype = 'NavSimplebar';
22585 for(var i=0;i< children.length;i++) {
22587 this.buttons.add(this.addxtypeChild(children[i]));
22591 editor.on('editorevent', this.updateToolbar, this);
22593 onBtnClick : function(id)
22595 this.editorcore.relayCmd(id);
22596 this.editorcore.focus();
22600 * Protected method that will not generally be called directly. It triggers
22601 * a toolbar update by reading the markup state of the current selection in the editor.
22603 updateToolbar: function(){
22605 if(!this.editorcore.activated){
22606 this.editor.onFirstFocus(); // is this neeed?
22610 var btns = this.buttons;
22611 var doc = this.editorcore.doc;
22612 btns.get('bold').setActive(doc.queryCommandState('bold'));
22613 btns.get('italic').setActive(doc.queryCommandState('italic'));
22614 //btns.get('underline').setActive(doc.queryCommandState('underline'));
22616 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22617 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22618 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22620 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22621 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22624 var ans = this.editorcore.getAllAncestors();
22625 if (this.formatCombo) {
22628 var store = this.formatCombo.store;
22629 this.formatCombo.setValue("");
22630 for (var i =0; i < ans.length;i++) {
22631 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22633 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22641 // hides menus... - so this cant be on a menu...
22642 Roo.bootstrap.MenuMgr.hideAll();
22644 Roo.bootstrap.MenuMgr.hideAll();
22645 //this.editorsyncValue();
22647 onFirstFocus: function() {
22648 this.buttons.each(function(item){
22652 toggleSourceEdit : function(sourceEditMode){
22655 if(sourceEditMode){
22656 Roo.log("disabling buttons");
22657 this.buttons.each( function(item){
22658 if(item.cmd != 'pencil'){
22664 Roo.log("enabling buttons");
22665 if(this.editorcore.initialized){
22666 this.buttons.each( function(item){
22672 Roo.log("calling toggole on editor");
22673 // tell the editor that it's been pressed..
22674 this.editor.toggleSourceEdit(sourceEditMode);
22684 * @class Roo.bootstrap.Table.AbstractSelectionModel
22685 * @extends Roo.util.Observable
22686 * Abstract base class for grid SelectionModels. It provides the interface that should be
22687 * implemented by descendant classes. This class should not be directly instantiated.
22690 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22691 this.locked = false;
22692 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22696 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22697 /** @ignore Called by the grid automatically. Do not call directly. */
22698 init : function(grid){
22704 * Locks the selections.
22707 this.locked = true;
22711 * Unlocks the selections.
22713 unlock : function(){
22714 this.locked = false;
22718 * Returns true if the selections are locked.
22719 * @return {Boolean}
22721 isLocked : function(){
22722 return this.locked;
22726 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22727 * @class Roo.bootstrap.Table.RowSelectionModel
22728 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22729 * It supports multiple selections and keyboard selection/navigation.
22731 * @param {Object} config
22734 Roo.bootstrap.Table.RowSelectionModel = function(config){
22735 Roo.apply(this, config);
22736 this.selections = new Roo.util.MixedCollection(false, function(o){
22741 this.lastActive = false;
22745 * @event selectionchange
22746 * Fires when the selection changes
22747 * @param {SelectionModel} this
22749 "selectionchange" : true,
22751 * @event afterselectionchange
22752 * Fires after the selection changes (eg. by key press or clicking)
22753 * @param {SelectionModel} this
22755 "afterselectionchange" : true,
22757 * @event beforerowselect
22758 * Fires when a row is selected being selected, return false to cancel.
22759 * @param {SelectionModel} this
22760 * @param {Number} rowIndex The selected index
22761 * @param {Boolean} keepExisting False if other selections will be cleared
22763 "beforerowselect" : true,
22766 * Fires when a row is selected.
22767 * @param {SelectionModel} this
22768 * @param {Number} rowIndex The selected index
22769 * @param {Roo.data.Record} r The record
22771 "rowselect" : true,
22773 * @event rowdeselect
22774 * Fires when a row is deselected.
22775 * @param {SelectionModel} this
22776 * @param {Number} rowIndex The selected index
22778 "rowdeselect" : true
22780 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22781 this.locked = false;
22784 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22786 * @cfg {Boolean} singleSelect
22787 * True to allow selection of only one row at a time (defaults to false)
22789 singleSelect : false,
22792 initEvents : function()
22795 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22796 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
22797 //}else{ // allow click to work like normal
22798 // this.grid.on("rowclick", this.handleDragableRowClick, this);
22800 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22801 this.grid.on("rowclick", this.handleMouseDown, this);
22803 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22804 "up" : function(e){
22806 this.selectPrevious(e.shiftKey);
22807 }else if(this.last !== false && this.lastActive !== false){
22808 var last = this.last;
22809 this.selectRange(this.last, this.lastActive-1);
22810 this.grid.getView().focusRow(this.lastActive);
22811 if(last !== false){
22815 this.selectFirstRow();
22817 this.fireEvent("afterselectionchange", this);
22819 "down" : function(e){
22821 this.selectNext(e.shiftKey);
22822 }else if(this.last !== false && this.lastActive !== false){
22823 var last = this.last;
22824 this.selectRange(this.last, this.lastActive+1);
22825 this.grid.getView().focusRow(this.lastActive);
22826 if(last !== false){
22830 this.selectFirstRow();
22832 this.fireEvent("afterselectionchange", this);
22836 this.grid.store.on('load', function(){
22837 this.selections.clear();
22840 var view = this.grid.view;
22841 view.on("refresh", this.onRefresh, this);
22842 view.on("rowupdated", this.onRowUpdated, this);
22843 view.on("rowremoved", this.onRemove, this);
22848 onRefresh : function()
22850 var ds = this.grid.store, i, v = this.grid.view;
22851 var s = this.selections;
22852 s.each(function(r){
22853 if((i = ds.indexOfId(r.id)) != -1){
22862 onRemove : function(v, index, r){
22863 this.selections.remove(r);
22867 onRowUpdated : function(v, index, r){
22868 if(this.isSelected(r)){
22869 v.onRowSelect(index);
22875 * @param {Array} records The records to select
22876 * @param {Boolean} keepExisting (optional) True to keep existing selections
22878 selectRecords : function(records, keepExisting)
22881 this.clearSelections();
22883 var ds = this.grid.store;
22884 for(var i = 0, len = records.length; i < len; i++){
22885 this.selectRow(ds.indexOf(records[i]), true);
22890 * Gets the number of selected rows.
22893 getCount : function(){
22894 return this.selections.length;
22898 * Selects the first row in the grid.
22900 selectFirstRow : function(){
22905 * Select the last row.
22906 * @param {Boolean} keepExisting (optional) True to keep existing selections
22908 selectLastRow : function(keepExisting){
22909 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22910 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22914 * Selects the row immediately following the last selected row.
22915 * @param {Boolean} keepExisting (optional) True to keep existing selections
22917 selectNext : function(keepExisting)
22919 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22920 this.selectRow(this.last+1, keepExisting);
22921 this.grid.getView().focusRow(this.last);
22926 * Selects the row that precedes the last selected row.
22927 * @param {Boolean} keepExisting (optional) True to keep existing selections
22929 selectPrevious : function(keepExisting){
22931 this.selectRow(this.last-1, keepExisting);
22932 this.grid.getView().focusRow(this.last);
22937 * Returns the selected records
22938 * @return {Array} Array of selected records
22940 getSelections : function(){
22941 return [].concat(this.selections.items);
22945 * Returns the first selected record.
22948 getSelected : function(){
22949 return this.selections.itemAt(0);
22954 * Clears all selections.
22956 clearSelections : function(fast)
22962 var ds = this.grid.store;
22963 var s = this.selections;
22964 s.each(function(r){
22965 this.deselectRow(ds.indexOfId(r.id));
22969 this.selections.clear();
22976 * Selects all rows.
22978 selectAll : function(){
22982 this.selections.clear();
22983 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
22984 this.selectRow(i, true);
22989 * Returns True if there is a selection.
22990 * @return {Boolean}
22992 hasSelection : function(){
22993 return this.selections.length > 0;
22997 * Returns True if the specified row is selected.
22998 * @param {Number/Record} record The record or index of the record to check
22999 * @return {Boolean}
23001 isSelected : function(index){
23002 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23003 return (r && this.selections.key(r.id) ? true : false);
23007 * Returns True if the specified record id is selected.
23008 * @param {String} id The id of record to check
23009 * @return {Boolean}
23011 isIdSelected : function(id){
23012 return (this.selections.key(id) ? true : false);
23017 handleMouseDBClick : function(e, t){
23021 handleMouseDown : function(e, t)
23023 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23024 if(this.isLocked() || rowIndex < 0 ){
23027 if(e.shiftKey && this.last !== false){
23028 var last = this.last;
23029 this.selectRange(last, rowIndex, e.ctrlKey);
23030 this.last = last; // reset the last
23034 var isSelected = this.isSelected(rowIndex);
23035 //Roo.log("select row:" + rowIndex);
23037 this.deselectRow(rowIndex);
23039 this.selectRow(rowIndex, true);
23043 if(e.button !== 0 && isSelected){
23044 alert('rowIndex 2: ' + rowIndex);
23045 view.focusRow(rowIndex);
23046 }else if(e.ctrlKey && isSelected){
23047 this.deselectRow(rowIndex);
23048 }else if(!isSelected){
23049 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23050 view.focusRow(rowIndex);
23054 this.fireEvent("afterselectionchange", this);
23057 handleDragableRowClick : function(grid, rowIndex, e)
23059 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23060 this.selectRow(rowIndex, false);
23061 grid.view.focusRow(rowIndex);
23062 this.fireEvent("afterselectionchange", this);
23067 * Selects multiple rows.
23068 * @param {Array} rows Array of the indexes of the row to select
23069 * @param {Boolean} keepExisting (optional) True to keep existing selections
23071 selectRows : function(rows, keepExisting){
23073 this.clearSelections();
23075 for(var i = 0, len = rows.length; i < len; i++){
23076 this.selectRow(rows[i], true);
23081 * Selects a range of rows. All rows in between startRow and endRow are also selected.
23082 * @param {Number} startRow The index of the first row in the range
23083 * @param {Number} endRow The index of the last row in the range
23084 * @param {Boolean} keepExisting (optional) True to retain existing selections
23086 selectRange : function(startRow, endRow, keepExisting){
23091 this.clearSelections();
23093 if(startRow <= endRow){
23094 for(var i = startRow; i <= endRow; i++){
23095 this.selectRow(i, true);
23098 for(var i = startRow; i >= endRow; i--){
23099 this.selectRow(i, true);
23105 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23106 * @param {Number} startRow The index of the first row in the range
23107 * @param {Number} endRow The index of the last row in the range
23109 deselectRange : function(startRow, endRow, preventViewNotify){
23113 for(var i = startRow; i <= endRow; i++){
23114 this.deselectRow(i, preventViewNotify);
23120 * @param {Number} row The index of the row to select
23121 * @param {Boolean} keepExisting (optional) True to keep existing selections
23123 selectRow : function(index, keepExisting, preventViewNotify)
23125 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23128 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23129 if(!keepExisting || this.singleSelect){
23130 this.clearSelections();
23133 var r = this.grid.store.getAt(index);
23134 //console.log('selectRow - record id :' + r.id);
23136 this.selections.add(r);
23137 this.last = this.lastActive = index;
23138 if(!preventViewNotify){
23139 var proxy = new Roo.Element(
23140 this.grid.getRowDom(index)
23142 proxy.addClass('bg-info info');
23144 this.fireEvent("rowselect", this, index, r);
23145 this.fireEvent("selectionchange", this);
23151 * @param {Number} row The index of the row to deselect
23153 deselectRow : function(index, preventViewNotify)
23158 if(this.last == index){
23161 if(this.lastActive == index){
23162 this.lastActive = false;
23165 var r = this.grid.store.getAt(index);
23170 this.selections.remove(r);
23171 //.console.log('deselectRow - record id :' + r.id);
23172 if(!preventViewNotify){
23174 var proxy = new Roo.Element(
23175 this.grid.getRowDom(index)
23177 proxy.removeClass('bg-info info');
23179 this.fireEvent("rowdeselect", this, index);
23180 this.fireEvent("selectionchange", this);
23184 restoreLast : function(){
23186 this.last = this._last;
23191 acceptsNav : function(row, col, cm){
23192 return !cm.isHidden(col) && cm.isCellEditable(col, row);
23196 onEditorKey : function(field, e){
23197 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23202 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23204 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23206 }else if(k == e.ENTER && !e.ctrlKey){
23210 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23212 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23214 }else if(k == e.ESC){
23218 g.startEditing(newCell[0], newCell[1]);
23224 * Ext JS Library 1.1.1
23225 * Copyright(c) 2006-2007, Ext JS, LLC.
23227 * Originally Released Under LGPL - original licence link has changed is not relivant.
23230 * <script type="text/javascript">
23234 * @class Roo.bootstrap.PagingToolbar
23235 * @extends Roo.bootstrap.NavSimplebar
23236 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23238 * Create a new PagingToolbar
23239 * @param {Object} config The config object
23240 * @param {Roo.data.Store} store
23242 Roo.bootstrap.PagingToolbar = function(config)
23244 // old args format still supported... - xtype is prefered..
23245 // created from xtype...
23247 this.ds = config.dataSource;
23249 if (config.store && !this.ds) {
23250 this.store= Roo.factory(config.store, Roo.data);
23251 this.ds = this.store;
23252 this.ds.xmodule = this.xmodule || false;
23255 this.toolbarItems = [];
23256 if (config.items) {
23257 this.toolbarItems = config.items;
23260 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23265 this.bind(this.ds);
23268 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23272 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23274 * @cfg {Roo.data.Store} dataSource
23275 * The underlying data store providing the paged data
23278 * @cfg {String/HTMLElement/Element} container
23279 * container The id or element that will contain the toolbar
23282 * @cfg {Boolean} displayInfo
23283 * True to display the displayMsg (defaults to false)
23286 * @cfg {Number} pageSize
23287 * The number of records to display per page (defaults to 20)
23291 * @cfg {String} displayMsg
23292 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23294 displayMsg : 'Displaying {0} - {1} of {2}',
23296 * @cfg {String} emptyMsg
23297 * The message to display when no records are found (defaults to "No data to display")
23299 emptyMsg : 'No data to display',
23301 * Customizable piece of the default paging text (defaults to "Page")
23304 beforePageText : "Page",
23306 * Customizable piece of the default paging text (defaults to "of %0")
23309 afterPageText : "of {0}",
23311 * Customizable piece of the default paging text (defaults to "First Page")
23314 firstText : "First Page",
23316 * Customizable piece of the default paging text (defaults to "Previous Page")
23319 prevText : "Previous Page",
23321 * Customizable piece of the default paging text (defaults to "Next Page")
23324 nextText : "Next Page",
23326 * Customizable piece of the default paging text (defaults to "Last Page")
23329 lastText : "Last Page",
23331 * Customizable piece of the default paging text (defaults to "Refresh")
23334 refreshText : "Refresh",
23338 onRender : function(ct, position)
23340 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23341 this.navgroup.parentId = this.id;
23342 this.navgroup.onRender(this.el, null);
23343 // add the buttons to the navgroup
23345 if(this.displayInfo){
23346 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23347 this.displayEl = this.el.select('.x-paging-info', true).first();
23348 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23349 // this.displayEl = navel.el.select('span',true).first();
23355 Roo.each(_this.buttons, function(e){ // this might need to use render????
23356 Roo.factory(e).onRender(_this.el, null);
23360 Roo.each(_this.toolbarItems, function(e) {
23361 _this.navgroup.addItem(e);
23365 this.first = this.navgroup.addItem({
23366 tooltip: this.firstText,
23368 icon : 'fa fa-backward',
23370 preventDefault: true,
23371 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23374 this.prev = this.navgroup.addItem({
23375 tooltip: this.prevText,
23377 icon : 'fa fa-step-backward',
23379 preventDefault: true,
23380 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
23382 //this.addSeparator();
23385 var field = this.navgroup.addItem( {
23387 cls : 'x-paging-position',
23389 html : this.beforePageText +
23390 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23391 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
23394 this.field = field.el.select('input', true).first();
23395 this.field.on("keydown", this.onPagingKeydown, this);
23396 this.field.on("focus", function(){this.dom.select();});
23399 this.afterTextEl = field.el.select('.x-paging-after',true).first();
23400 //this.field.setHeight(18);
23401 //this.addSeparator();
23402 this.next = this.navgroup.addItem({
23403 tooltip: this.nextText,
23405 html : ' <i class="fa fa-step-forward">',
23407 preventDefault: true,
23408 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
23410 this.last = this.navgroup.addItem({
23411 tooltip: this.lastText,
23412 icon : 'fa fa-forward',
23415 preventDefault: true,
23416 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
23418 //this.addSeparator();
23419 this.loading = this.navgroup.addItem({
23420 tooltip: this.refreshText,
23421 icon: 'fa fa-refresh',
23422 preventDefault: true,
23423 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23429 updateInfo : function(){
23430 if(this.displayEl){
23431 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23432 var msg = count == 0 ?
23436 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
23438 this.displayEl.update(msg);
23443 onLoad : function(ds, r, o){
23444 this.cursor = o.params ? o.params.start : 0;
23445 var d = this.getPageData(),
23449 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23450 this.field.dom.value = ap;
23451 this.first.setDisabled(ap == 1);
23452 this.prev.setDisabled(ap == 1);
23453 this.next.setDisabled(ap == ps);
23454 this.last.setDisabled(ap == ps);
23455 this.loading.enable();
23460 getPageData : function(){
23461 var total = this.ds.getTotalCount();
23464 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23465 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23470 onLoadError : function(){
23471 this.loading.enable();
23475 onPagingKeydown : function(e){
23476 var k = e.getKey();
23477 var d = this.getPageData();
23479 var v = this.field.dom.value, pageNum;
23480 if(!v || isNaN(pageNum = parseInt(v, 10))){
23481 this.field.dom.value = d.activePage;
23484 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23485 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23488 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))
23490 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23491 this.field.dom.value = pageNum;
23492 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23495 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23497 var v = this.field.dom.value, pageNum;
23498 var increment = (e.shiftKey) ? 10 : 1;
23499 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23502 if(!v || isNaN(pageNum = parseInt(v, 10))) {
23503 this.field.dom.value = d.activePage;
23506 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23508 this.field.dom.value = parseInt(v, 10) + increment;
23509 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23510 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23517 beforeLoad : function(){
23519 this.loading.disable();
23524 onClick : function(which){
23533 ds.load({params:{start: 0, limit: this.pageSize}});
23536 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23539 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23542 var total = ds.getTotalCount();
23543 var extra = total % this.pageSize;
23544 var lastStart = extra ? (total - extra) : total-this.pageSize;
23545 ds.load({params:{start: lastStart, limit: this.pageSize}});
23548 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23554 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23555 * @param {Roo.data.Store} store The data store to unbind
23557 unbind : function(ds){
23558 ds.un("beforeload", this.beforeLoad, this);
23559 ds.un("load", this.onLoad, this);
23560 ds.un("loadexception", this.onLoadError, this);
23561 ds.un("remove", this.updateInfo, this);
23562 ds.un("add", this.updateInfo, this);
23563 this.ds = undefined;
23567 * Binds the paging toolbar to the specified {@link Roo.data.Store}
23568 * @param {Roo.data.Store} store The data store to bind
23570 bind : function(ds){
23571 ds.on("beforeload", this.beforeLoad, this);
23572 ds.on("load", this.onLoad, this);
23573 ds.on("loadexception", this.onLoadError, this);
23574 ds.on("remove", this.updateInfo, this);
23575 ds.on("add", this.updateInfo, this);
23586 * @class Roo.bootstrap.MessageBar
23587 * @extends Roo.bootstrap.Component
23588 * Bootstrap MessageBar class
23589 * @cfg {String} html contents of the MessageBar
23590 * @cfg {String} weight (info | success | warning | danger) default info
23591 * @cfg {String} beforeClass insert the bar before the given class
23592 * @cfg {Boolean} closable (true | false) default false
23593 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23596 * Create a new Element
23597 * @param {Object} config The config object
23600 Roo.bootstrap.MessageBar = function(config){
23601 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23604 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
23610 beforeClass: 'bootstrap-sticky-wrap',
23612 getAutoCreate : function(){
23616 cls: 'alert alert-dismissable alert-' + this.weight,
23621 html: this.html || ''
23627 cfg.cls += ' alert-messages-fixed';
23641 onRender : function(ct, position)
23643 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23646 var cfg = Roo.apply({}, this.getAutoCreate());
23650 cfg.cls += ' ' + this.cls;
23653 cfg.style = this.style;
23655 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23657 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23660 this.el.select('>button.close').on('click', this.hide, this);
23666 if (!this.rendered) {
23672 this.fireEvent('show', this);
23678 if (!this.rendered) {
23684 this.fireEvent('hide', this);
23687 update : function()
23689 // var e = this.el.dom.firstChild;
23691 // if(this.closable){
23692 // e = e.nextSibling;
23695 // e.data = this.html || '';
23697 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23713 * @class Roo.bootstrap.Graph
23714 * @extends Roo.bootstrap.Component
23715 * Bootstrap Graph class
23719 @cfg {String} graphtype bar | vbar | pie
23720 @cfg {number} g_x coodinator | centre x (pie)
23721 @cfg {number} g_y coodinator | centre y (pie)
23722 @cfg {number} g_r radius (pie)
23723 @cfg {number} g_height height of the chart (respected by all elements in the set)
23724 @cfg {number} g_width width of the chart (respected by all elements in the set)
23725 @cfg {Object} title The title of the chart
23728 -opts (object) options for the chart
23730 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23731 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23733 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.
23734 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23736 o stretch (boolean)
23738 -opts (object) options for the pie
23741 o startAngle (number)
23742 o endAngle (number)
23746 * Create a new Input
23747 * @param {Object} config The config object
23750 Roo.bootstrap.Graph = function(config){
23751 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23757 * The img click event for the img.
23758 * @param {Roo.EventObject} e
23764 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23775 //g_colors: this.colors,
23782 getAutoCreate : function(){
23793 onRender : function(ct,position){
23796 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23798 if (typeof(Raphael) == 'undefined') {
23799 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23803 this.raphael = Raphael(this.el.dom);
23805 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23806 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23807 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23808 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23810 r.text(160, 10, "Single Series Chart").attr(txtattr);
23811 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23812 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23813 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23815 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23816 r.barchart(330, 10, 300, 220, data1);
23817 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23818 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23821 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23822 // r.barchart(30, 30, 560, 250, xdata, {
23823 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23824 // axis : "0 0 1 1",
23825 // axisxlabels : xdata
23826 // //yvalues : cols,
23829 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23831 // this.load(null,xdata,{
23832 // axis : "0 0 1 1",
23833 // axisxlabels : xdata
23838 load : function(graphtype,xdata,opts)
23840 this.raphael.clear();
23842 graphtype = this.graphtype;
23847 var r = this.raphael,
23848 fin = function () {
23849 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23851 fout = function () {
23852 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23854 pfin = function() {
23855 this.sector.stop();
23856 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23859 this.label[0].stop();
23860 this.label[0].attr({ r: 7.5 });
23861 this.label[1].attr({ "font-weight": 800 });
23864 pfout = function() {
23865 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23868 this.label[0].animate({ r: 5 }, 500, "bounce");
23869 this.label[1].attr({ "font-weight": 400 });
23875 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23878 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23881 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23882 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23884 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23891 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23896 setTitle: function(o)
23901 initEvents: function() {
23904 this.el.on('click', this.onClick, this);
23908 onClick : function(e)
23910 Roo.log('img onclick');
23911 this.fireEvent('click', this, e);
23923 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23926 * @class Roo.bootstrap.dash.NumberBox
23927 * @extends Roo.bootstrap.Component
23928 * Bootstrap NumberBox class
23929 * @cfg {String} headline Box headline
23930 * @cfg {String} content Box content
23931 * @cfg {String} icon Box icon
23932 * @cfg {String} footer Footer text
23933 * @cfg {String} fhref Footer href
23936 * Create a new NumberBox
23937 * @param {Object} config The config object
23941 Roo.bootstrap.dash.NumberBox = function(config){
23942 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23946 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23955 getAutoCreate : function(){
23959 cls : 'small-box ',
23967 cls : 'roo-headline',
23968 html : this.headline
23972 cls : 'roo-content',
23973 html : this.content
23987 cls : 'ion ' + this.icon
23996 cls : 'small-box-footer',
23997 href : this.fhref || '#',
24001 cfg.cn.push(footer);
24008 onRender : function(ct,position){
24009 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24016 setHeadline: function (value)
24018 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24021 setFooter: function (value, href)
24023 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24026 this.el.select('a.small-box-footer',true).first().attr('href', href);
24031 setContent: function (value)
24033 this.el.select('.roo-content',true).first().dom.innerHTML = value;
24036 initEvents: function()
24050 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24053 * @class Roo.bootstrap.dash.TabBox
24054 * @extends Roo.bootstrap.Component
24055 * Bootstrap TabBox class
24056 * @cfg {String} title Title of the TabBox
24057 * @cfg {String} icon Icon of the TabBox
24058 * @cfg {Boolean} showtabs (true|false) show the tabs default true
24059 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24062 * Create a new TabBox
24063 * @param {Object} config The config object
24067 Roo.bootstrap.dash.TabBox = function(config){
24068 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24073 * When a pane is added
24074 * @param {Roo.bootstrap.dash.TabPane} pane
24078 * @event activatepane
24079 * When a pane is activated
24080 * @param {Roo.bootstrap.dash.TabPane} pane
24082 "activatepane" : true
24090 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
24095 tabScrollable : false,
24097 getChildContainer : function()
24099 return this.el.select('.tab-content', true).first();
24102 getAutoCreate : function(){
24106 cls: 'pull-left header',
24114 cls: 'fa ' + this.icon
24120 cls: 'nav nav-tabs pull-right',
24126 if(this.tabScrollable){
24133 cls: 'nav nav-tabs pull-right',
24144 cls: 'nav-tabs-custom',
24149 cls: 'tab-content no-padding',
24157 initEvents : function()
24159 //Roo.log('add add pane handler');
24160 this.on('addpane', this.onAddPane, this);
24163 * Updates the box title
24164 * @param {String} html to set the title to.
24166 setTitle : function(value)
24168 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24170 onAddPane : function(pane)
24172 this.panes.push(pane);
24173 //Roo.log('addpane');
24175 // tabs are rendere left to right..
24176 if(!this.showtabs){
24180 var ctr = this.el.select('.nav-tabs', true).first();
24183 var existing = ctr.select('.nav-tab',true);
24184 var qty = existing.getCount();;
24187 var tab = ctr.createChild({
24189 cls : 'nav-tab' + (qty ? '' : ' active'),
24197 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24200 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24202 pane.el.addClass('active');
24207 onTabClick : function(ev,un,ob,pane)
24209 //Roo.log('tab - prev default');
24210 ev.preventDefault();
24213 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24214 pane.tab.addClass('active');
24215 //Roo.log(pane.title);
24216 this.getChildContainer().select('.tab-pane',true).removeClass('active');
24217 // technically we should have a deactivate event.. but maybe add later.
24218 // and it should not de-activate the selected tab...
24219 this.fireEvent('activatepane', pane);
24220 pane.el.addClass('active');
24221 pane.fireEvent('activate');
24226 getActivePane : function()
24229 Roo.each(this.panes, function(p) {
24230 if(p.el.hasClass('active')){
24251 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24253 * @class Roo.bootstrap.TabPane
24254 * @extends Roo.bootstrap.Component
24255 * Bootstrap TabPane class
24256 * @cfg {Boolean} active (false | true) Default false
24257 * @cfg {String} title title of panel
24261 * Create a new TabPane
24262 * @param {Object} config The config object
24265 Roo.bootstrap.dash.TabPane = function(config){
24266 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24272 * When a pane is activated
24273 * @param {Roo.bootstrap.dash.TabPane} pane
24280 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
24285 // the tabBox that this is attached to.
24288 getAutoCreate : function()
24296 cfg.cls += ' active';
24301 initEvents : function()
24303 //Roo.log('trigger add pane handler');
24304 this.parent().fireEvent('addpane', this)
24308 * Updates the tab title
24309 * @param {String} html to set the title to.
24311 setTitle: function(str)
24317 this.tab.select('a', true).first().dom.innerHTML = str;
24334 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24337 * @class Roo.bootstrap.menu.Menu
24338 * @extends Roo.bootstrap.Component
24339 * Bootstrap Menu class - container for Menu
24340 * @cfg {String} html Text of the menu
24341 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24342 * @cfg {String} icon Font awesome icon
24343 * @cfg {String} pos Menu align to (top | bottom) default bottom
24347 * Create a new Menu
24348 * @param {Object} config The config object
24352 Roo.bootstrap.menu.Menu = function(config){
24353 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24357 * @event beforeshow
24358 * Fires before this menu is displayed
24359 * @param {Roo.bootstrap.menu.Menu} this
24363 * @event beforehide
24364 * Fires before this menu is hidden
24365 * @param {Roo.bootstrap.menu.Menu} this
24370 * Fires after this menu is displayed
24371 * @param {Roo.bootstrap.menu.Menu} this
24376 * Fires after this menu is hidden
24377 * @param {Roo.bootstrap.menu.Menu} this
24382 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24383 * @param {Roo.bootstrap.menu.Menu} this
24384 * @param {Roo.EventObject} e
24391 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
24395 weight : 'default',
24400 getChildContainer : function() {
24401 if(this.isSubMenu){
24405 return this.el.select('ul.dropdown-menu', true).first();
24408 getAutoCreate : function()
24413 cls : 'roo-menu-text',
24421 cls : 'fa ' + this.icon
24432 cls : 'dropdown-button btn btn-' + this.weight,
24437 cls : 'dropdown-toggle btn btn-' + this.weight,
24447 cls : 'dropdown-menu'
24453 if(this.pos == 'top'){
24454 cfg.cls += ' dropup';
24457 if(this.isSubMenu){
24460 cls : 'dropdown-menu'
24467 onRender : function(ct, position)
24469 this.isSubMenu = ct.hasClass('dropdown-submenu');
24471 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24474 initEvents : function()
24476 if(this.isSubMenu){
24480 this.hidden = true;
24482 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24483 this.triggerEl.on('click', this.onTriggerPress, this);
24485 this.buttonEl = this.el.select('button.dropdown-button', true).first();
24486 this.buttonEl.on('click', this.onClick, this);
24492 if(this.isSubMenu){
24496 return this.el.select('ul.dropdown-menu', true).first();
24499 onClick : function(e)
24501 this.fireEvent("click", this, e);
24504 onTriggerPress : function(e)
24506 if (this.isVisible()) {
24513 isVisible : function(){
24514 return !this.hidden;
24519 this.fireEvent("beforeshow", this);
24521 this.hidden = false;
24522 this.el.addClass('open');
24524 Roo.get(document).on("mouseup", this.onMouseUp, this);
24526 this.fireEvent("show", this);
24533 this.fireEvent("beforehide", this);
24535 this.hidden = true;
24536 this.el.removeClass('open');
24538 Roo.get(document).un("mouseup", this.onMouseUp);
24540 this.fireEvent("hide", this);
24543 onMouseUp : function()
24557 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24560 * @class Roo.bootstrap.menu.Item
24561 * @extends Roo.bootstrap.Component
24562 * Bootstrap MenuItem class
24563 * @cfg {Boolean} submenu (true | false) default false
24564 * @cfg {String} html text of the item
24565 * @cfg {String} href the link
24566 * @cfg {Boolean} disable (true | false) default false
24567 * @cfg {Boolean} preventDefault (true | false) default true
24568 * @cfg {String} icon Font awesome icon
24569 * @cfg {String} pos Submenu align to (left | right) default right
24573 * Create a new Item
24574 * @param {Object} config The config object
24578 Roo.bootstrap.menu.Item = function(config){
24579 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24583 * Fires when the mouse is hovering over this menu
24584 * @param {Roo.bootstrap.menu.Item} this
24585 * @param {Roo.EventObject} e
24590 * Fires when the mouse exits this menu
24591 * @param {Roo.bootstrap.menu.Item} this
24592 * @param {Roo.EventObject} e
24598 * The raw click event for the entire grid.
24599 * @param {Roo.EventObject} e
24605 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
24610 preventDefault: true,
24615 getAutoCreate : function()
24620 cls : 'roo-menu-item-text',
24628 cls : 'fa ' + this.icon
24637 href : this.href || '#',
24644 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24648 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24650 if(this.pos == 'left'){
24651 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24658 initEvents : function()
24660 this.el.on('mouseover', this.onMouseOver, this);
24661 this.el.on('mouseout', this.onMouseOut, this);
24663 this.el.select('a', true).first().on('click', this.onClick, this);
24667 onClick : function(e)
24669 if(this.preventDefault){
24670 e.preventDefault();
24673 this.fireEvent("click", this, e);
24676 onMouseOver : function(e)
24678 if(this.submenu && this.pos == 'left'){
24679 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24682 this.fireEvent("mouseover", this, e);
24685 onMouseOut : function(e)
24687 this.fireEvent("mouseout", this, e);
24699 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24702 * @class Roo.bootstrap.menu.Separator
24703 * @extends Roo.bootstrap.Component
24704 * Bootstrap Separator class
24707 * Create a new Separator
24708 * @param {Object} config The config object
24712 Roo.bootstrap.menu.Separator = function(config){
24713 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24716 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24718 getAutoCreate : function(){
24739 * @class Roo.bootstrap.Tooltip
24740 * Bootstrap Tooltip class
24741 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24742 * to determine which dom element triggers the tooltip.
24744 * It needs to add support for additional attributes like tooltip-position
24747 * Create a new Toolti
24748 * @param {Object} config The config object
24751 Roo.bootstrap.Tooltip = function(config){
24752 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24755 Roo.apply(Roo.bootstrap.Tooltip, {
24757 * @function init initialize tooltip monitoring.
24761 currentTip : false,
24762 currentRegion : false,
24768 Roo.get(document).on('mouseover', this.enter ,this);
24769 Roo.get(document).on('mouseout', this.leave, this);
24772 this.currentTip = new Roo.bootstrap.Tooltip();
24775 enter : function(ev)
24777 var dom = ev.getTarget();
24779 //Roo.log(['enter',dom]);
24780 var el = Roo.fly(dom);
24781 if (this.currentEl) {
24783 //Roo.log(this.currentEl);
24784 //Roo.log(this.currentEl.contains(dom));
24785 if (this.currentEl == el) {
24788 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24794 if (this.currentTip.el) {
24795 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24799 if(!el || el.dom == document){
24805 // you can not look for children, as if el is the body.. then everythign is the child..
24806 if (!el.attr('tooltip')) { //
24807 if (!el.select("[tooltip]").elements.length) {
24810 // is the mouse over this child...?
24811 bindEl = el.select("[tooltip]").first();
24812 var xy = ev.getXY();
24813 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24814 //Roo.log("not in region.");
24817 //Roo.log("child element over..");
24820 this.currentEl = bindEl;
24821 this.currentTip.bind(bindEl);
24822 this.currentRegion = Roo.lib.Region.getRegion(dom);
24823 this.currentTip.enter();
24826 leave : function(ev)
24828 var dom = ev.getTarget();
24829 //Roo.log(['leave',dom]);
24830 if (!this.currentEl) {
24835 if (dom != this.currentEl.dom) {
24838 var xy = ev.getXY();
24839 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24842 // only activate leave if mouse cursor is outside... bounding box..
24847 if (this.currentTip) {
24848 this.currentTip.leave();
24850 //Roo.log('clear currentEl');
24851 this.currentEl = false;
24856 'left' : ['r-l', [-2,0], 'right'],
24857 'right' : ['l-r', [2,0], 'left'],
24858 'bottom' : ['t-b', [0,2], 'top'],
24859 'top' : [ 'b-t', [0,-2], 'bottom']
24865 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24870 delay : null, // can be { show : 300 , hide: 500}
24874 hoverState : null, //???
24876 placement : 'bottom',
24878 getAutoCreate : function(){
24885 cls : 'tooltip-arrow'
24888 cls : 'tooltip-inner'
24895 bind : function(el)
24901 enter : function () {
24903 if (this.timeout != null) {
24904 clearTimeout(this.timeout);
24907 this.hoverState = 'in';
24908 //Roo.log("enter - show");
24909 if (!this.delay || !this.delay.show) {
24914 this.timeout = setTimeout(function () {
24915 if (_t.hoverState == 'in') {
24918 }, this.delay.show);
24922 clearTimeout(this.timeout);
24924 this.hoverState = 'out';
24925 if (!this.delay || !this.delay.hide) {
24931 this.timeout = setTimeout(function () {
24932 //Roo.log("leave - timeout");
24934 if (_t.hoverState == 'out') {
24936 Roo.bootstrap.Tooltip.currentEl = false;
24944 this.render(document.body);
24947 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24949 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24951 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24953 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24955 var placement = typeof this.placement == 'function' ?
24956 this.placement.call(this, this.el, on_el) :
24959 var autoToken = /\s?auto?\s?/i;
24960 var autoPlace = autoToken.test(placement);
24962 placement = placement.replace(autoToken, '') || 'top';
24966 //this.el.setXY([0,0]);
24968 //this.el.dom.style.display='block';
24970 //this.el.appendTo(on_el);
24972 var p = this.getPosition();
24973 var box = this.el.getBox();
24979 var align = Roo.bootstrap.Tooltip.alignment[placement];
24981 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24983 if(placement == 'top' || placement == 'bottom'){
24985 placement = 'right';
24988 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24989 placement = 'left';
24992 var scroll = Roo.select('body', true).first().getScroll();
24994 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25000 align = Roo.bootstrap.Tooltip.alignment[placement];
25002 this.el.alignTo(this.bindEl, align[0],align[1]);
25003 //var arrow = this.el.select('.arrow',true).first();
25004 //arrow.set(align[2],
25006 this.el.addClass(placement);
25008 this.el.addClass('in fade');
25010 this.hoverState = null;
25012 if (this.el.hasClass('fade')) {
25023 //this.el.setXY([0,0]);
25024 this.el.removeClass('in');
25040 * @class Roo.bootstrap.LocationPicker
25041 * @extends Roo.bootstrap.Component
25042 * Bootstrap LocationPicker class
25043 * @cfg {Number} latitude Position when init default 0
25044 * @cfg {Number} longitude Position when init default 0
25045 * @cfg {Number} zoom default 15
25046 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25047 * @cfg {Boolean} mapTypeControl default false
25048 * @cfg {Boolean} disableDoubleClickZoom default false
25049 * @cfg {Boolean} scrollwheel default true
25050 * @cfg {Boolean} streetViewControl default false
25051 * @cfg {Number} radius default 0
25052 * @cfg {String} locationName
25053 * @cfg {Boolean} draggable default true
25054 * @cfg {Boolean} enableAutocomplete default false
25055 * @cfg {Boolean} enableReverseGeocode default true
25056 * @cfg {String} markerTitle
25059 * Create a new LocationPicker
25060 * @param {Object} config The config object
25064 Roo.bootstrap.LocationPicker = function(config){
25066 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25071 * Fires when the picker initialized.
25072 * @param {Roo.bootstrap.LocationPicker} this
25073 * @param {Google Location} location
25077 * @event positionchanged
25078 * Fires when the picker position changed.
25079 * @param {Roo.bootstrap.LocationPicker} this
25080 * @param {Google Location} location
25082 positionchanged : true,
25085 * Fires when the map resize.
25086 * @param {Roo.bootstrap.LocationPicker} this
25091 * Fires when the map show.
25092 * @param {Roo.bootstrap.LocationPicker} this
25097 * Fires when the map hide.
25098 * @param {Roo.bootstrap.LocationPicker} this
25103 * Fires when click the map.
25104 * @param {Roo.bootstrap.LocationPicker} this
25105 * @param {Map event} e
25109 * @event mapRightClick
25110 * Fires when right click the map.
25111 * @param {Roo.bootstrap.LocationPicker} this
25112 * @param {Map event} e
25114 mapRightClick : true,
25116 * @event markerClick
25117 * Fires when click the marker.
25118 * @param {Roo.bootstrap.LocationPicker} this
25119 * @param {Map event} e
25121 markerClick : true,
25123 * @event markerRightClick
25124 * Fires when right click the marker.
25125 * @param {Roo.bootstrap.LocationPicker} this
25126 * @param {Map event} e
25128 markerRightClick : true,
25130 * @event OverlayViewDraw
25131 * Fires when OverlayView Draw
25132 * @param {Roo.bootstrap.LocationPicker} this
25134 OverlayViewDraw : true,
25136 * @event OverlayViewOnAdd
25137 * Fires when OverlayView Draw
25138 * @param {Roo.bootstrap.LocationPicker} this
25140 OverlayViewOnAdd : true,
25142 * @event OverlayViewOnRemove
25143 * Fires when OverlayView Draw
25144 * @param {Roo.bootstrap.LocationPicker} this
25146 OverlayViewOnRemove : true,
25148 * @event OverlayViewShow
25149 * Fires when OverlayView Draw
25150 * @param {Roo.bootstrap.LocationPicker} this
25151 * @param {Pixel} cpx
25153 OverlayViewShow : true,
25155 * @event OverlayViewHide
25156 * Fires when OverlayView Draw
25157 * @param {Roo.bootstrap.LocationPicker} this
25159 OverlayViewHide : true,
25161 * @event loadexception
25162 * Fires when load google lib failed.
25163 * @param {Roo.bootstrap.LocationPicker} this
25165 loadexception : true
25170 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
25172 gMapContext: false,
25178 mapTypeControl: false,
25179 disableDoubleClickZoom: false,
25181 streetViewControl: false,
25185 enableAutocomplete: false,
25186 enableReverseGeocode: true,
25189 getAutoCreate: function()
25194 cls: 'roo-location-picker'
25200 initEvents: function(ct, position)
25202 if(!this.el.getWidth() || this.isApplied()){
25206 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25211 initial: function()
25213 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25214 this.fireEvent('loadexception', this);
25218 if(!this.mapTypeId){
25219 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25222 this.gMapContext = this.GMapContext();
25224 this.initOverlayView();
25226 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25230 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25231 _this.setPosition(_this.gMapContext.marker.position);
25234 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25235 _this.fireEvent('mapClick', this, event);
25239 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25240 _this.fireEvent('mapRightClick', this, event);
25244 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25245 _this.fireEvent('markerClick', this, event);
25249 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25250 _this.fireEvent('markerRightClick', this, event);
25254 this.setPosition(this.gMapContext.location);
25256 this.fireEvent('initial', this, this.gMapContext.location);
25259 initOverlayView: function()
25263 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25267 _this.fireEvent('OverlayViewDraw', _this);
25272 _this.fireEvent('OverlayViewOnAdd', _this);
25275 onRemove: function()
25277 _this.fireEvent('OverlayViewOnRemove', _this);
25280 show: function(cpx)
25282 _this.fireEvent('OverlayViewShow', _this, cpx);
25287 _this.fireEvent('OverlayViewHide', _this);
25293 fromLatLngToContainerPixel: function(event)
25295 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25298 isApplied: function()
25300 return this.getGmapContext() == false ? false : true;
25303 getGmapContext: function()
25305 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25308 GMapContext: function()
25310 var position = new google.maps.LatLng(this.latitude, this.longitude);
25312 var _map = new google.maps.Map(this.el.dom, {
25315 mapTypeId: this.mapTypeId,
25316 mapTypeControl: this.mapTypeControl,
25317 disableDoubleClickZoom: this.disableDoubleClickZoom,
25318 scrollwheel: this.scrollwheel,
25319 streetViewControl: this.streetViewControl,
25320 locationName: this.locationName,
25321 draggable: this.draggable,
25322 enableAutocomplete: this.enableAutocomplete,
25323 enableReverseGeocode: this.enableReverseGeocode
25326 var _marker = new google.maps.Marker({
25327 position: position,
25329 title: this.markerTitle,
25330 draggable: this.draggable
25337 location: position,
25338 radius: this.radius,
25339 locationName: this.locationName,
25340 addressComponents: {
25341 formatted_address: null,
25342 addressLine1: null,
25343 addressLine2: null,
25345 streetNumber: null,
25349 stateOrProvince: null
25352 domContainer: this.el.dom,
25353 geodecoder: new google.maps.Geocoder()
25357 drawCircle: function(center, radius, options)
25359 if (this.gMapContext.circle != null) {
25360 this.gMapContext.circle.setMap(null);
25364 options = Roo.apply({}, options, {
25365 strokeColor: "#0000FF",
25366 strokeOpacity: .35,
25368 fillColor: "#0000FF",
25372 options.map = this.gMapContext.map;
25373 options.radius = radius;
25374 options.center = center;
25375 this.gMapContext.circle = new google.maps.Circle(options);
25376 return this.gMapContext.circle;
25382 setPosition: function(location)
25384 this.gMapContext.location = location;
25385 this.gMapContext.marker.setPosition(location);
25386 this.gMapContext.map.panTo(location);
25387 this.drawCircle(location, this.gMapContext.radius, {});
25391 if (this.gMapContext.settings.enableReverseGeocode) {
25392 this.gMapContext.geodecoder.geocode({
25393 latLng: this.gMapContext.location
25394 }, function(results, status) {
25396 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25397 _this.gMapContext.locationName = results[0].formatted_address;
25398 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25400 _this.fireEvent('positionchanged', this, location);
25407 this.fireEvent('positionchanged', this, location);
25412 google.maps.event.trigger(this.gMapContext.map, "resize");
25414 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25416 this.fireEvent('resize', this);
25419 setPositionByLatLng: function(latitude, longitude)
25421 this.setPosition(new google.maps.LatLng(latitude, longitude));
25424 getCurrentPosition: function()
25427 latitude: this.gMapContext.location.lat(),
25428 longitude: this.gMapContext.location.lng()
25432 getAddressName: function()
25434 return this.gMapContext.locationName;
25437 getAddressComponents: function()
25439 return this.gMapContext.addressComponents;
25442 address_component_from_google_geocode: function(address_components)
25446 for (var i = 0; i < address_components.length; i++) {
25447 var component = address_components[i];
25448 if (component.types.indexOf("postal_code") >= 0) {
25449 result.postalCode = component.short_name;
25450 } else if (component.types.indexOf("street_number") >= 0) {
25451 result.streetNumber = component.short_name;
25452 } else if (component.types.indexOf("route") >= 0) {
25453 result.streetName = component.short_name;
25454 } else if (component.types.indexOf("neighborhood") >= 0) {
25455 result.city = component.short_name;
25456 } else if (component.types.indexOf("locality") >= 0) {
25457 result.city = component.short_name;
25458 } else if (component.types.indexOf("sublocality") >= 0) {
25459 result.district = component.short_name;
25460 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25461 result.stateOrProvince = component.short_name;
25462 } else if (component.types.indexOf("country") >= 0) {
25463 result.country = component.short_name;
25467 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25468 result.addressLine2 = "";
25472 setZoomLevel: function(zoom)
25474 this.gMapContext.map.setZoom(zoom);
25487 this.fireEvent('show', this);
25498 this.fireEvent('hide', this);
25503 Roo.apply(Roo.bootstrap.LocationPicker, {
25505 OverlayView : function(map, options)
25507 options = options || {};
25521 * @class Roo.bootstrap.Alert
25522 * @extends Roo.bootstrap.Component
25523 * Bootstrap Alert class
25524 * @cfg {String} title The title of alert
25525 * @cfg {String} html The content of alert
25526 * @cfg {String} weight ( success | info | warning | danger )
25527 * @cfg {String} faicon font-awesomeicon
25530 * Create a new alert
25531 * @param {Object} config The config object
25535 Roo.bootstrap.Alert = function(config){
25536 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25540 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
25547 getAutoCreate : function()
25556 cls : 'roo-alert-icon'
25561 cls : 'roo-alert-title',
25566 cls : 'roo-alert-text',
25573 cfg.cn[0].cls += ' fa ' + this.faicon;
25577 cfg.cls += ' alert-' + this.weight;
25583 initEvents: function()
25585 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25588 setTitle : function(str)
25590 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25593 setText : function(str)
25595 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25598 setWeight : function(weight)
25601 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25604 this.weight = weight;
25606 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25609 setIcon : function(icon)
25612 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25615 this.faicon = icon;
25617 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25638 * @class Roo.bootstrap.UploadCropbox
25639 * @extends Roo.bootstrap.Component
25640 * Bootstrap UploadCropbox class
25641 * @cfg {String} emptyText show when image has been loaded
25642 * @cfg {String} rotateNotify show when image too small to rotate
25643 * @cfg {Number} errorTimeout default 3000
25644 * @cfg {Number} minWidth default 300
25645 * @cfg {Number} minHeight default 300
25646 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25647 * @cfg {Boolean} isDocument (true|false) default false
25648 * @cfg {String} url action url
25649 * @cfg {String} paramName default 'imageUpload'
25650 * @cfg {String} method default POST
25651 * @cfg {Boolean} loadMask (true|false) default true
25652 * @cfg {Boolean} loadingText default 'Loading...'
25655 * Create a new UploadCropbox
25656 * @param {Object} config The config object
25659 Roo.bootstrap.UploadCropbox = function(config){
25660 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25664 * @event beforeselectfile
25665 * Fire before select file
25666 * @param {Roo.bootstrap.UploadCropbox} this
25668 "beforeselectfile" : true,
25671 * Fire after initEvent
25672 * @param {Roo.bootstrap.UploadCropbox} this
25677 * Fire after initEvent
25678 * @param {Roo.bootstrap.UploadCropbox} this
25679 * @param {String} data
25684 * Fire when preparing the file data
25685 * @param {Roo.bootstrap.UploadCropbox} this
25686 * @param {Object} file
25691 * Fire when get exception
25692 * @param {Roo.bootstrap.UploadCropbox} this
25693 * @param {XMLHttpRequest} xhr
25695 "exception" : true,
25697 * @event beforeloadcanvas
25698 * Fire before load the canvas
25699 * @param {Roo.bootstrap.UploadCropbox} this
25700 * @param {String} src
25702 "beforeloadcanvas" : true,
25705 * Fire when trash image
25706 * @param {Roo.bootstrap.UploadCropbox} this
25711 * Fire when download the image
25712 * @param {Roo.bootstrap.UploadCropbox} this
25716 * @event footerbuttonclick
25717 * Fire when footerbuttonclick
25718 * @param {Roo.bootstrap.UploadCropbox} this
25719 * @param {String} type
25721 "footerbuttonclick" : true,
25725 * @param {Roo.bootstrap.UploadCropbox} this
25730 * Fire when rotate the image
25731 * @param {Roo.bootstrap.UploadCropbox} this
25732 * @param {String} pos
25737 * Fire when inspect the file
25738 * @param {Roo.bootstrap.UploadCropbox} this
25739 * @param {Object} file
25744 * Fire when xhr upload the file
25745 * @param {Roo.bootstrap.UploadCropbox} this
25746 * @param {Object} data
25751 * Fire when arrange the file data
25752 * @param {Roo.bootstrap.UploadCropbox} this
25753 * @param {Object} formData
25758 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25761 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25763 emptyText : 'Click to upload image',
25764 rotateNotify : 'Image is too small to rotate',
25765 errorTimeout : 3000,
25779 cropType : 'image/jpeg',
25781 canvasLoaded : false,
25782 isDocument : false,
25784 paramName : 'imageUpload',
25786 loadingText : 'Loading...',
25789 getAutoCreate : function()
25793 cls : 'roo-upload-cropbox',
25797 cls : 'roo-upload-cropbox-selector',
25802 cls : 'roo-upload-cropbox-body',
25803 style : 'cursor:pointer',
25807 cls : 'roo-upload-cropbox-preview'
25811 cls : 'roo-upload-cropbox-thumb'
25815 cls : 'roo-upload-cropbox-empty-notify',
25816 html : this.emptyText
25820 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25821 html : this.rotateNotify
25827 cls : 'roo-upload-cropbox-footer',
25830 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25840 onRender : function(ct, position)
25842 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25844 if (this.buttons.length) {
25846 Roo.each(this.buttons, function(bb) {
25848 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25850 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25856 this.maskEl = this.el;
25860 initEvents : function()
25862 this.urlAPI = (window.createObjectURL && window) ||
25863 (window.URL && URL.revokeObjectURL && URL) ||
25864 (window.webkitURL && webkitURL);
25866 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25867 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25869 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25870 this.selectorEl.hide();
25872 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25873 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25875 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25876 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25877 this.thumbEl.hide();
25879 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25880 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25882 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25883 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25884 this.errorEl.hide();
25886 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25887 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25888 this.footerEl.hide();
25890 this.setThumbBoxSize();
25896 this.fireEvent('initial', this);
25903 window.addEventListener("resize", function() { _this.resize(); } );
25905 this.bodyEl.on('click', this.beforeSelectFile, this);
25908 this.bodyEl.on('touchstart', this.onTouchStart, this);
25909 this.bodyEl.on('touchmove', this.onTouchMove, this);
25910 this.bodyEl.on('touchend', this.onTouchEnd, this);
25914 this.bodyEl.on('mousedown', this.onMouseDown, this);
25915 this.bodyEl.on('mousemove', this.onMouseMove, this);
25916 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25917 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25918 Roo.get(document).on('mouseup', this.onMouseUp, this);
25921 this.selectorEl.on('change', this.onFileSelected, this);
25927 this.baseScale = 1;
25929 this.baseRotate = 1;
25930 this.dragable = false;
25931 this.pinching = false;
25934 this.cropData = false;
25935 this.notifyEl.dom.innerHTML = this.emptyText;
25937 this.selectorEl.dom.value = '';
25941 resize : function()
25943 if(this.fireEvent('resize', this) != false){
25944 this.setThumbBoxPosition();
25945 this.setCanvasPosition();
25949 onFooterButtonClick : function(e, el, o, type)
25952 case 'rotate-left' :
25953 this.onRotateLeft(e);
25955 case 'rotate-right' :
25956 this.onRotateRight(e);
25959 this.beforeSelectFile(e);
25974 this.fireEvent('footerbuttonclick', this, type);
25977 beforeSelectFile : function(e)
25979 e.preventDefault();
25981 if(this.fireEvent('beforeselectfile', this) != false){
25982 this.selectorEl.dom.click();
25986 onFileSelected : function(e)
25988 e.preventDefault();
25990 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25994 var file = this.selectorEl.dom.files[0];
25996 if(this.fireEvent('inspect', this, file) != false){
25997 this.prepare(file);
26002 trash : function(e)
26004 this.fireEvent('trash', this);
26007 download : function(e)
26009 this.fireEvent('download', this);
26012 loadCanvas : function(src)
26014 if(this.fireEvent('beforeloadcanvas', this, src) != false){
26018 this.imageEl = document.createElement('img');
26022 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26024 this.imageEl.src = src;
26028 onLoadCanvas : function()
26030 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26031 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26033 this.bodyEl.un('click', this.beforeSelectFile, this);
26035 this.notifyEl.hide();
26036 this.thumbEl.show();
26037 this.footerEl.show();
26039 this.baseRotateLevel();
26041 if(this.isDocument){
26042 this.setThumbBoxSize();
26045 this.setThumbBoxPosition();
26047 this.baseScaleLevel();
26053 this.canvasLoaded = true;
26056 this.maskEl.unmask();
26061 setCanvasPosition : function()
26063 if(!this.canvasEl){
26067 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26068 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26070 this.previewEl.setLeft(pw);
26071 this.previewEl.setTop(ph);
26075 onMouseDown : function(e)
26079 this.dragable = true;
26080 this.pinching = false;
26082 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26083 this.dragable = false;
26087 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26088 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26092 onMouseMove : function(e)
26096 if(!this.canvasLoaded){
26100 if (!this.dragable){
26104 var minX = Math.ceil(this.thumbEl.getLeft(true));
26105 var minY = Math.ceil(this.thumbEl.getTop(true));
26107 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26108 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26110 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26111 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26113 x = x - this.mouseX;
26114 y = y - this.mouseY;
26116 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26117 var bgY = Math.ceil(y + this.previewEl.getTop(true));
26119 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26120 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26122 this.previewEl.setLeft(bgX);
26123 this.previewEl.setTop(bgY);
26125 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26126 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26129 onMouseUp : function(e)
26133 this.dragable = false;
26136 onMouseWheel : function(e)
26140 this.startScale = this.scale;
26142 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26144 if(!this.zoomable()){
26145 this.scale = this.startScale;
26154 zoomable : function()
26156 var minScale = this.thumbEl.getWidth() / this.minWidth;
26158 if(this.minWidth < this.minHeight){
26159 minScale = this.thumbEl.getHeight() / this.minHeight;
26162 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26163 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26167 (this.rotate == 0 || this.rotate == 180) &&
26169 width > this.imageEl.OriginWidth ||
26170 height > this.imageEl.OriginHeight ||
26171 (width < this.minWidth && height < this.minHeight)
26179 (this.rotate == 90 || this.rotate == 270) &&
26181 width > this.imageEl.OriginWidth ||
26182 height > this.imageEl.OriginHeight ||
26183 (width < this.minHeight && height < this.minWidth)
26190 !this.isDocument &&
26191 (this.rotate == 0 || this.rotate == 180) &&
26193 width < this.minWidth ||
26194 width > this.imageEl.OriginWidth ||
26195 height < this.minHeight ||
26196 height > this.imageEl.OriginHeight
26203 !this.isDocument &&
26204 (this.rotate == 90 || this.rotate == 270) &&
26206 width < this.minHeight ||
26207 width > this.imageEl.OriginWidth ||
26208 height < this.minWidth ||
26209 height > this.imageEl.OriginHeight
26219 onRotateLeft : function(e)
26221 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26223 var minScale = this.thumbEl.getWidth() / this.minWidth;
26225 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26226 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26228 this.startScale = this.scale;
26230 while (this.getScaleLevel() < minScale){
26232 this.scale = this.scale + 1;
26234 if(!this.zoomable()){
26239 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26240 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26245 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26252 this.scale = this.startScale;
26254 this.onRotateFail();
26259 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26261 if(this.isDocument){
26262 this.setThumbBoxSize();
26263 this.setThumbBoxPosition();
26264 this.setCanvasPosition();
26269 this.fireEvent('rotate', this, 'left');
26273 onRotateRight : function(e)
26275 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26277 var minScale = this.thumbEl.getWidth() / this.minWidth;
26279 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26280 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26282 this.startScale = this.scale;
26284 while (this.getScaleLevel() < minScale){
26286 this.scale = this.scale + 1;
26288 if(!this.zoomable()){
26293 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26294 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26299 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26306 this.scale = this.startScale;
26308 this.onRotateFail();
26313 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26315 if(this.isDocument){
26316 this.setThumbBoxSize();
26317 this.setThumbBoxPosition();
26318 this.setCanvasPosition();
26323 this.fireEvent('rotate', this, 'right');
26326 onRotateFail : function()
26328 this.errorEl.show(true);
26332 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26337 this.previewEl.dom.innerHTML = '';
26339 var canvasEl = document.createElement("canvas");
26341 var contextEl = canvasEl.getContext("2d");
26343 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26344 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26345 var center = this.imageEl.OriginWidth / 2;
26347 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26348 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26349 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26350 center = this.imageEl.OriginHeight / 2;
26353 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26355 contextEl.translate(center, center);
26356 contextEl.rotate(this.rotate * Math.PI / 180);
26358 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26360 this.canvasEl = document.createElement("canvas");
26362 this.contextEl = this.canvasEl.getContext("2d");
26364 switch (this.rotate) {
26367 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26368 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26370 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26375 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26376 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26378 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26379 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);
26383 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26388 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26389 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26391 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26392 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);
26396 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);
26401 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26402 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26404 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26405 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26409 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);
26416 this.previewEl.appendChild(this.canvasEl);
26418 this.setCanvasPosition();
26423 if(!this.canvasLoaded){
26427 var imageCanvas = document.createElement("canvas");
26429 var imageContext = imageCanvas.getContext("2d");
26431 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26432 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26434 var center = imageCanvas.width / 2;
26436 imageContext.translate(center, center);
26438 imageContext.rotate(this.rotate * Math.PI / 180);
26440 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26442 var canvas = document.createElement("canvas");
26444 var context = canvas.getContext("2d");
26446 canvas.width = this.minWidth;
26447 canvas.height = this.minHeight;
26449 switch (this.rotate) {
26452 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26453 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26455 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26456 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26458 var targetWidth = this.minWidth - 2 * x;
26459 var targetHeight = this.minHeight - 2 * y;
26463 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26464 scale = targetWidth / width;
26467 if(x > 0 && y == 0){
26468 scale = targetHeight / height;
26471 if(x > 0 && y > 0){
26472 scale = targetWidth / width;
26474 if(width < height){
26475 scale = targetHeight / height;
26479 context.scale(scale, scale);
26481 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26482 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26484 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26485 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26487 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26492 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26493 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26495 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26496 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26498 var targetWidth = this.minWidth - 2 * x;
26499 var targetHeight = this.minHeight - 2 * y;
26503 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26504 scale = targetWidth / width;
26507 if(x > 0 && y == 0){
26508 scale = targetHeight / height;
26511 if(x > 0 && y > 0){
26512 scale = targetWidth / width;
26514 if(width < height){
26515 scale = targetHeight / height;
26519 context.scale(scale, scale);
26521 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26522 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26524 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26525 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26527 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26529 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26534 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26535 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26537 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26538 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26540 var targetWidth = this.minWidth - 2 * x;
26541 var targetHeight = this.minHeight - 2 * y;
26545 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26546 scale = targetWidth / width;
26549 if(x > 0 && y == 0){
26550 scale = targetHeight / height;
26553 if(x > 0 && y > 0){
26554 scale = targetWidth / width;
26556 if(width < height){
26557 scale = targetHeight / height;
26561 context.scale(scale, scale);
26563 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26564 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26566 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26567 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26569 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26570 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26572 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26577 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26578 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26580 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26581 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26583 var targetWidth = this.minWidth - 2 * x;
26584 var targetHeight = this.minHeight - 2 * y;
26588 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26589 scale = targetWidth / width;
26592 if(x > 0 && y == 0){
26593 scale = targetHeight / height;
26596 if(x > 0 && y > 0){
26597 scale = targetWidth / width;
26599 if(width < height){
26600 scale = targetHeight / height;
26604 context.scale(scale, scale);
26606 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26607 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26609 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26610 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26612 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26614 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26621 this.cropData = canvas.toDataURL(this.cropType);
26623 if(this.fireEvent('crop', this, this.cropData) !== false){
26624 this.process(this.file, this.cropData);
26631 setThumbBoxSize : function()
26635 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26636 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26637 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26639 this.minWidth = width;
26640 this.minHeight = height;
26642 if(this.rotate == 90 || this.rotate == 270){
26643 this.minWidth = height;
26644 this.minHeight = width;
26649 width = Math.ceil(this.minWidth * height / this.minHeight);
26651 if(this.minWidth > this.minHeight){
26653 height = Math.ceil(this.minHeight * width / this.minWidth);
26656 this.thumbEl.setStyle({
26657 width : width + 'px',
26658 height : height + 'px'
26665 setThumbBoxPosition : function()
26667 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26668 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26670 this.thumbEl.setLeft(x);
26671 this.thumbEl.setTop(y);
26675 baseRotateLevel : function()
26677 this.baseRotate = 1;
26680 typeof(this.exif) != 'undefined' &&
26681 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26682 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26684 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26687 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26691 baseScaleLevel : function()
26695 if(this.isDocument){
26697 if(this.baseRotate == 6 || this.baseRotate == 8){
26699 height = this.thumbEl.getHeight();
26700 this.baseScale = height / this.imageEl.OriginWidth;
26702 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26703 width = this.thumbEl.getWidth();
26704 this.baseScale = width / this.imageEl.OriginHeight;
26710 height = this.thumbEl.getHeight();
26711 this.baseScale = height / this.imageEl.OriginHeight;
26713 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26714 width = this.thumbEl.getWidth();
26715 this.baseScale = width / this.imageEl.OriginWidth;
26721 if(this.baseRotate == 6 || this.baseRotate == 8){
26723 width = this.thumbEl.getHeight();
26724 this.baseScale = width / this.imageEl.OriginHeight;
26726 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26727 height = this.thumbEl.getWidth();
26728 this.baseScale = height / this.imageEl.OriginHeight;
26731 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26732 height = this.thumbEl.getWidth();
26733 this.baseScale = height / this.imageEl.OriginHeight;
26735 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26736 width = this.thumbEl.getHeight();
26737 this.baseScale = width / this.imageEl.OriginWidth;
26744 width = this.thumbEl.getWidth();
26745 this.baseScale = width / this.imageEl.OriginWidth;
26747 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26748 height = this.thumbEl.getHeight();
26749 this.baseScale = height / this.imageEl.OriginHeight;
26752 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26754 height = this.thumbEl.getHeight();
26755 this.baseScale = height / this.imageEl.OriginHeight;
26757 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26758 width = this.thumbEl.getWidth();
26759 this.baseScale = width / this.imageEl.OriginWidth;
26767 getScaleLevel : function()
26769 return this.baseScale * Math.pow(1.1, this.scale);
26772 onTouchStart : function(e)
26774 if(!this.canvasLoaded){
26775 this.beforeSelectFile(e);
26779 var touches = e.browserEvent.touches;
26785 if(touches.length == 1){
26786 this.onMouseDown(e);
26790 if(touches.length != 2){
26796 for(var i = 0, finger; finger = touches[i]; i++){
26797 coords.push(finger.pageX, finger.pageY);
26800 var x = Math.pow(coords[0] - coords[2], 2);
26801 var y = Math.pow(coords[1] - coords[3], 2);
26803 this.startDistance = Math.sqrt(x + y);
26805 this.startScale = this.scale;
26807 this.pinching = true;
26808 this.dragable = false;
26812 onTouchMove : function(e)
26814 if(!this.pinching && !this.dragable){
26818 var touches = e.browserEvent.touches;
26825 this.onMouseMove(e);
26831 for(var i = 0, finger; finger = touches[i]; i++){
26832 coords.push(finger.pageX, finger.pageY);
26835 var x = Math.pow(coords[0] - coords[2], 2);
26836 var y = Math.pow(coords[1] - coords[3], 2);
26838 this.endDistance = Math.sqrt(x + y);
26840 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26842 if(!this.zoomable()){
26843 this.scale = this.startScale;
26851 onTouchEnd : function(e)
26853 this.pinching = false;
26854 this.dragable = false;
26858 process : function(file, crop)
26861 this.maskEl.mask(this.loadingText);
26864 this.xhr = new XMLHttpRequest();
26866 file.xhr = this.xhr;
26868 this.xhr.open(this.method, this.url, true);
26871 "Accept": "application/json",
26872 "Cache-Control": "no-cache",
26873 "X-Requested-With": "XMLHttpRequest"
26876 for (var headerName in headers) {
26877 var headerValue = headers[headerName];
26879 this.xhr.setRequestHeader(headerName, headerValue);
26885 this.xhr.onload = function()
26887 _this.xhrOnLoad(_this.xhr);
26890 this.xhr.onerror = function()
26892 _this.xhrOnError(_this.xhr);
26895 var formData = new FormData();
26897 formData.append('returnHTML', 'NO');
26900 formData.append('crop', crop);
26903 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26904 formData.append(this.paramName, file, file.name);
26907 if(typeof(file.filename) != 'undefined'){
26908 formData.append('filename', file.filename);
26911 if(typeof(file.mimetype) != 'undefined'){
26912 formData.append('mimetype', file.mimetype);
26915 if(this.fireEvent('arrange', this, formData) != false){
26916 this.xhr.send(formData);
26920 xhrOnLoad : function(xhr)
26923 this.maskEl.unmask();
26926 if (xhr.readyState !== 4) {
26927 this.fireEvent('exception', this, xhr);
26931 var response = Roo.decode(xhr.responseText);
26933 if(!response.success){
26934 this.fireEvent('exception', this, xhr);
26938 var response = Roo.decode(xhr.responseText);
26940 this.fireEvent('upload', this, response);
26944 xhrOnError : function()
26947 this.maskEl.unmask();
26950 Roo.log('xhr on error');
26952 var response = Roo.decode(xhr.responseText);
26958 prepare : function(file)
26961 this.maskEl.mask(this.loadingText);
26967 if(typeof(file) === 'string'){
26968 this.loadCanvas(file);
26972 if(!file || !this.urlAPI){
26977 this.cropType = file.type;
26981 if(this.fireEvent('prepare', this, this.file) != false){
26983 var reader = new FileReader();
26985 reader.onload = function (e) {
26986 if (e.target.error) {
26987 Roo.log(e.target.error);
26991 var buffer = e.target.result,
26992 dataView = new DataView(buffer),
26994 maxOffset = dataView.byteLength - 4,
26998 if (dataView.getUint16(0) === 0xffd8) {
26999 while (offset < maxOffset) {
27000 markerBytes = dataView.getUint16(offset);
27002 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27003 markerLength = dataView.getUint16(offset + 2) + 2;
27004 if (offset + markerLength > dataView.byteLength) {
27005 Roo.log('Invalid meta data: Invalid segment size.');
27009 if(markerBytes == 0xffe1){
27010 _this.parseExifData(
27017 offset += markerLength;
27027 var url = _this.urlAPI.createObjectURL(_this.file);
27029 _this.loadCanvas(url);
27034 reader.readAsArrayBuffer(this.file);
27040 parseExifData : function(dataView, offset, length)
27042 var tiffOffset = offset + 10,
27046 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27047 // No Exif data, might be XMP data instead
27051 // Check for the ASCII code for "Exif" (0x45786966):
27052 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27053 // No Exif data, might be XMP data instead
27056 if (tiffOffset + 8 > dataView.byteLength) {
27057 Roo.log('Invalid Exif data: Invalid segment size.');
27060 // Check for the two null bytes:
27061 if (dataView.getUint16(offset + 8) !== 0x0000) {
27062 Roo.log('Invalid Exif data: Missing byte alignment offset.');
27065 // Check the byte alignment:
27066 switch (dataView.getUint16(tiffOffset)) {
27068 littleEndian = true;
27071 littleEndian = false;
27074 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27077 // Check for the TIFF tag marker (0x002A):
27078 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27079 Roo.log('Invalid Exif data: Missing TIFF marker.');
27082 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27083 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27085 this.parseExifTags(
27088 tiffOffset + dirOffset,
27093 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27098 if (dirOffset + 6 > dataView.byteLength) {
27099 Roo.log('Invalid Exif data: Invalid directory offset.');
27102 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27103 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27104 if (dirEndOffset + 4 > dataView.byteLength) {
27105 Roo.log('Invalid Exif data: Invalid directory size.');
27108 for (i = 0; i < tagsNumber; i += 1) {
27112 dirOffset + 2 + 12 * i, // tag offset
27116 // Return the offset to the next directory:
27117 return dataView.getUint32(dirEndOffset, littleEndian);
27120 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
27122 var tag = dataView.getUint16(offset, littleEndian);
27124 this.exif[tag] = this.getExifValue(
27128 dataView.getUint16(offset + 2, littleEndian), // tag type
27129 dataView.getUint32(offset + 4, littleEndian), // tag length
27134 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27136 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27145 Roo.log('Invalid Exif data: Invalid tag type.');
27149 tagSize = tagType.size * length;
27150 // Determine if the value is contained in the dataOffset bytes,
27151 // or if the value at the dataOffset is a pointer to the actual data:
27152 dataOffset = tagSize > 4 ?
27153 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27154 if (dataOffset + tagSize > dataView.byteLength) {
27155 Roo.log('Invalid Exif data: Invalid data offset.');
27158 if (length === 1) {
27159 return tagType.getValue(dataView, dataOffset, littleEndian);
27162 for (i = 0; i < length; i += 1) {
27163 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27166 if (tagType.ascii) {
27168 // Concatenate the chars:
27169 for (i = 0; i < values.length; i += 1) {
27171 // Ignore the terminating NULL byte(s):
27172 if (c === '\u0000') {
27184 Roo.apply(Roo.bootstrap.UploadCropbox, {
27186 'Orientation': 0x0112
27190 1: 0, //'top-left',
27192 3: 180, //'bottom-right',
27193 // 4: 'bottom-left',
27195 6: 90, //'right-top',
27196 // 7: 'right-bottom',
27197 8: 270 //'left-bottom'
27201 // byte, 8-bit unsigned int:
27203 getValue: function (dataView, dataOffset) {
27204 return dataView.getUint8(dataOffset);
27208 // ascii, 8-bit byte:
27210 getValue: function (dataView, dataOffset) {
27211 return String.fromCharCode(dataView.getUint8(dataOffset));
27216 // short, 16 bit int:
27218 getValue: function (dataView, dataOffset, littleEndian) {
27219 return dataView.getUint16(dataOffset, littleEndian);
27223 // long, 32 bit int:
27225 getValue: function (dataView, dataOffset, littleEndian) {
27226 return dataView.getUint32(dataOffset, littleEndian);
27230 // rational = two long values, first is numerator, second is denominator:
27232 getValue: function (dataView, dataOffset, littleEndian) {
27233 return dataView.getUint32(dataOffset, littleEndian) /
27234 dataView.getUint32(dataOffset + 4, littleEndian);
27238 // slong, 32 bit signed int:
27240 getValue: function (dataView, dataOffset, littleEndian) {
27241 return dataView.getInt32(dataOffset, littleEndian);
27245 // srational, two slongs, first is numerator, second is denominator:
27247 getValue: function (dataView, dataOffset, littleEndian) {
27248 return dataView.getInt32(dataOffset, littleEndian) /
27249 dataView.getInt32(dataOffset + 4, littleEndian);
27259 cls : 'btn-group roo-upload-cropbox-rotate-left',
27260 action : 'rotate-left',
27264 cls : 'btn btn-default',
27265 html : '<i class="fa fa-undo"></i>'
27271 cls : 'btn-group roo-upload-cropbox-picture',
27272 action : 'picture',
27276 cls : 'btn btn-default',
27277 html : '<i class="fa fa-picture-o"></i>'
27283 cls : 'btn-group roo-upload-cropbox-rotate-right',
27284 action : 'rotate-right',
27288 cls : 'btn btn-default',
27289 html : '<i class="fa fa-repeat"></i>'
27297 cls : 'btn-group roo-upload-cropbox-rotate-left',
27298 action : 'rotate-left',
27302 cls : 'btn btn-default',
27303 html : '<i class="fa fa-undo"></i>'
27309 cls : 'btn-group roo-upload-cropbox-download',
27310 action : 'download',
27314 cls : 'btn btn-default',
27315 html : '<i class="fa fa-download"></i>'
27321 cls : 'btn-group roo-upload-cropbox-crop',
27326 cls : 'btn btn-default',
27327 html : '<i class="fa fa-crop"></i>'
27333 cls : 'btn-group roo-upload-cropbox-trash',
27338 cls : 'btn btn-default',
27339 html : '<i class="fa fa-trash"></i>'
27345 cls : 'btn-group roo-upload-cropbox-rotate-right',
27346 action : 'rotate-right',
27350 cls : 'btn btn-default',
27351 html : '<i class="fa fa-repeat"></i>'
27359 cls : 'btn-group roo-upload-cropbox-rotate-left',
27360 action : 'rotate-left',
27364 cls : 'btn btn-default',
27365 html : '<i class="fa fa-undo"></i>'
27371 cls : 'btn-group roo-upload-cropbox-rotate-right',
27372 action : 'rotate-right',
27376 cls : 'btn btn-default',
27377 html : '<i class="fa fa-repeat"></i>'
27390 * @class Roo.bootstrap.DocumentManager
27391 * @extends Roo.bootstrap.Component
27392 * Bootstrap DocumentManager class
27393 * @cfg {String} paramName default 'imageUpload'
27394 * @cfg {String} method default POST
27395 * @cfg {String} url action url
27396 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27397 * @cfg {Boolean} multiple multiple upload default true
27398 * @cfg {Number} thumbSize default 300
27399 * @cfg {String} fieldLabel
27400 * @cfg {Number} labelWidth default 4
27401 * @cfg {String} labelAlign (left|top) default left
27402 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27405 * Create a new DocumentManager
27406 * @param {Object} config The config object
27409 Roo.bootstrap.DocumentManager = function(config){
27410 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27413 this.delegates = [];
27418 * Fire when initial the DocumentManager
27419 * @param {Roo.bootstrap.DocumentManager} this
27424 * inspect selected file
27425 * @param {Roo.bootstrap.DocumentManager} this
27426 * @param {File} file
27431 * Fire when xhr load exception
27432 * @param {Roo.bootstrap.DocumentManager} this
27433 * @param {XMLHttpRequest} xhr
27435 "exception" : true,
27437 * @event afterupload
27438 * Fire when xhr load exception
27439 * @param {Roo.bootstrap.DocumentManager} this
27440 * @param {XMLHttpRequest} xhr
27442 "afterupload" : true,
27445 * prepare the form data
27446 * @param {Roo.bootstrap.DocumentManager} this
27447 * @param {Object} formData
27452 * Fire when remove the file
27453 * @param {Roo.bootstrap.DocumentManager} this
27454 * @param {Object} file
27459 * Fire after refresh the file
27460 * @param {Roo.bootstrap.DocumentManager} this
27465 * Fire after click the image
27466 * @param {Roo.bootstrap.DocumentManager} this
27467 * @param {Object} file
27472 * Fire when upload a image and editable set to true
27473 * @param {Roo.bootstrap.DocumentManager} this
27474 * @param {Object} file
27478 * @event beforeselectfile
27479 * Fire before select file
27480 * @param {Roo.bootstrap.DocumentManager} this
27482 "beforeselectfile" : true,
27485 * Fire before process file
27486 * @param {Roo.bootstrap.DocumentManager} this
27487 * @param {Object} file
27494 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
27503 paramName : 'imageUpload',
27506 labelAlign : 'left',
27513 getAutoCreate : function()
27515 var managerWidget = {
27517 cls : 'roo-document-manager',
27521 cls : 'roo-document-manager-selector',
27526 cls : 'roo-document-manager-uploader',
27530 cls : 'roo-document-manager-upload-btn',
27531 html : '<i class="fa fa-plus"></i>'
27542 cls : 'column col-md-12',
27547 if(this.fieldLabel.length){
27552 cls : 'column col-md-12',
27553 html : this.fieldLabel
27557 cls : 'column col-md-12',
27562 if(this.labelAlign == 'left'){
27566 cls : 'column col-md-' + this.labelWidth,
27567 html : this.fieldLabel
27571 cls : 'column col-md-' + (12 - this.labelWidth),
27581 cls : 'row clearfix',
27589 initEvents : function()
27591 this.managerEl = this.el.select('.roo-document-manager', true).first();
27592 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27594 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27595 this.selectorEl.hide();
27598 this.selectorEl.attr('multiple', 'multiple');
27601 this.selectorEl.on('change', this.onFileSelected, this);
27603 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27604 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27606 this.uploader.on('click', this.onUploaderClick, this);
27608 this.renderProgressDialog();
27612 window.addEventListener("resize", function() { _this.refresh(); } );
27614 this.fireEvent('initial', this);
27617 renderProgressDialog : function()
27621 this.progressDialog = new Roo.bootstrap.Modal({
27622 cls : 'roo-document-manager-progress-dialog',
27623 allow_close : false,
27633 btnclick : function() {
27634 _this.uploadCancel();
27640 this.progressDialog.render(Roo.get(document.body));
27642 this.progress = new Roo.bootstrap.Progress({
27643 cls : 'roo-document-manager-progress',
27648 this.progress.render(this.progressDialog.getChildContainer());
27650 this.progressBar = new Roo.bootstrap.ProgressBar({
27651 cls : 'roo-document-manager-progress-bar',
27654 aria_valuemax : 12,
27658 this.progressBar.render(this.progress.getChildContainer());
27661 onUploaderClick : function(e)
27663 e.preventDefault();
27665 if(this.fireEvent('beforeselectfile', this) != false){
27666 this.selectorEl.dom.click();
27671 onFileSelected : function(e)
27673 e.preventDefault();
27675 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27679 Roo.each(this.selectorEl.dom.files, function(file){
27680 if(this.fireEvent('inspect', this, file) != false){
27681 this.files.push(file);
27691 this.selectorEl.dom.value = '';
27693 if(!this.files.length){
27697 if(this.boxes > 0 && this.files.length > this.boxes){
27698 this.files = this.files.slice(0, this.boxes);
27701 this.uploader.show();
27703 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27704 this.uploader.hide();
27713 Roo.each(this.files, function(file){
27715 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27716 var f = this.renderPreview(file);
27721 if(file.type.indexOf('image') != -1){
27722 this.delegates.push(
27724 _this.process(file);
27725 }).createDelegate(this)
27733 _this.process(file);
27734 }).createDelegate(this)
27739 this.files = files;
27741 this.delegates = this.delegates.concat(docs);
27743 if(!this.delegates.length){
27748 this.progressBar.aria_valuemax = this.delegates.length;
27755 arrange : function()
27757 if(!this.delegates.length){
27758 this.progressDialog.hide();
27763 var delegate = this.delegates.shift();
27765 this.progressDialog.show();
27767 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27769 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27774 refresh : function()
27776 this.uploader.show();
27778 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27779 this.uploader.hide();
27782 Roo.isTouch ? this.closable(false) : this.closable(true);
27784 this.fireEvent('refresh', this);
27787 onRemove : function(e, el, o)
27789 e.preventDefault();
27791 this.fireEvent('remove', this, o);
27795 remove : function(o)
27799 Roo.each(this.files, function(file){
27800 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27809 this.files = files;
27816 Roo.each(this.files, function(file){
27821 file.target.remove();
27830 onClick : function(e, el, o)
27832 e.preventDefault();
27834 this.fireEvent('click', this, o);
27838 closable : function(closable)
27840 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27842 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27854 xhrOnLoad : function(xhr)
27856 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27860 if (xhr.readyState !== 4) {
27862 this.fireEvent('exception', this, xhr);
27866 var response = Roo.decode(xhr.responseText);
27868 if(!response.success){
27870 this.fireEvent('exception', this, xhr);
27874 var file = this.renderPreview(response.data);
27876 this.files.push(file);
27880 this.fireEvent('afterupload', this, xhr);
27884 xhrOnError : function(xhr)
27886 Roo.log('xhr on error');
27888 var response = Roo.decode(xhr.responseText);
27895 process : function(file)
27897 if(this.fireEvent('process', this, file) !== false){
27898 if(this.editable && file.type.indexOf('image') != -1){
27899 this.fireEvent('edit', this, file);
27903 this.uploadStart(file, false);
27910 uploadStart : function(file, crop)
27912 this.xhr = new XMLHttpRequest();
27914 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27919 file.xhr = this.xhr;
27921 this.managerEl.createChild({
27923 cls : 'roo-document-manager-loading',
27927 tooltip : file.name,
27928 cls : 'roo-document-manager-thumb',
27929 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27935 this.xhr.open(this.method, this.url, true);
27938 "Accept": "application/json",
27939 "Cache-Control": "no-cache",
27940 "X-Requested-With": "XMLHttpRequest"
27943 for (var headerName in headers) {
27944 var headerValue = headers[headerName];
27946 this.xhr.setRequestHeader(headerName, headerValue);
27952 this.xhr.onload = function()
27954 _this.xhrOnLoad(_this.xhr);
27957 this.xhr.onerror = function()
27959 _this.xhrOnError(_this.xhr);
27962 var formData = new FormData();
27964 formData.append('returnHTML', 'NO');
27967 formData.append('crop', crop);
27970 formData.append(this.paramName, file, file.name);
27972 if(this.fireEvent('prepare', this, formData) != false){
27973 this.xhr.send(formData);
27977 uploadCancel : function()
27984 this.delegates = [];
27986 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27993 renderPreview : function(file)
27995 if(typeof(file.target) != 'undefined' && file.target){
27999 var previewEl = this.managerEl.createChild({
28001 cls : 'roo-document-manager-preview',
28005 tooltip : file.filename,
28006 cls : 'roo-document-manager-thumb',
28007 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28012 html : '<i class="fa fa-times-circle"></i>'
28017 var close = previewEl.select('button.close', true).first();
28019 close.on('click', this.onRemove, this, file);
28021 file.target = previewEl;
28023 var image = previewEl.select('img', true).first();
28027 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28029 image.on('click', this.onClick, this, file);
28035 onPreviewLoad : function(file, image)
28037 if(typeof(file.target) == 'undefined' || !file.target){
28041 var width = image.dom.naturalWidth || image.dom.width;
28042 var height = image.dom.naturalHeight || image.dom.height;
28044 if(width > height){
28045 file.target.addClass('wide');
28049 file.target.addClass('tall');
28054 uploadFromSource : function(file, crop)
28056 this.xhr = new XMLHttpRequest();
28058 this.managerEl.createChild({
28060 cls : 'roo-document-manager-loading',
28064 tooltip : file.name,
28065 cls : 'roo-document-manager-thumb',
28066 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28072 this.xhr.open(this.method, this.url, true);
28075 "Accept": "application/json",
28076 "Cache-Control": "no-cache",
28077 "X-Requested-With": "XMLHttpRequest"
28080 for (var headerName in headers) {
28081 var headerValue = headers[headerName];
28083 this.xhr.setRequestHeader(headerName, headerValue);
28089 this.xhr.onload = function()
28091 _this.xhrOnLoad(_this.xhr);
28094 this.xhr.onerror = function()
28096 _this.xhrOnError(_this.xhr);
28099 var formData = new FormData();
28101 formData.append('returnHTML', 'NO');
28103 formData.append('crop', crop);
28105 if(typeof(file.filename) != 'undefined'){
28106 formData.append('filename', file.filename);
28109 if(typeof(file.mimetype) != 'undefined'){
28110 formData.append('mimetype', file.mimetype);
28113 if(this.fireEvent('prepare', this, formData) != false){
28114 this.xhr.send(formData);
28124 * @class Roo.bootstrap.DocumentViewer
28125 * @extends Roo.bootstrap.Component
28126 * Bootstrap DocumentViewer class
28127 * @cfg {Boolean} showDownload (true|false) show download button (default true)
28128 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28131 * Create a new DocumentViewer
28132 * @param {Object} config The config object
28135 Roo.bootstrap.DocumentViewer = function(config){
28136 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28141 * Fire after initEvent
28142 * @param {Roo.bootstrap.DocumentViewer} this
28148 * @param {Roo.bootstrap.DocumentViewer} this
28153 * Fire after download button
28154 * @param {Roo.bootstrap.DocumentViewer} this
28159 * Fire after trash button
28160 * @param {Roo.bootstrap.DocumentViewer} this
28167 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
28169 showDownload : true,
28173 getAutoCreate : function()
28177 cls : 'roo-document-viewer',
28181 cls : 'roo-document-viewer-body',
28185 cls : 'roo-document-viewer-thumb',
28189 cls : 'roo-document-viewer-image'
28197 cls : 'roo-document-viewer-footer',
28200 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28204 cls : 'btn-group roo-document-viewer-download',
28208 cls : 'btn btn-default',
28209 html : '<i class="fa fa-download"></i>'
28215 cls : 'btn-group roo-document-viewer-trash',
28219 cls : 'btn btn-default',
28220 html : '<i class="fa fa-trash"></i>'
28233 initEvents : function()
28236 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28237 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28239 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28240 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28242 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28243 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28245 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28246 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28248 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28249 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28251 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28252 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28254 this.bodyEl.on('click', this.onClick, this);
28255 this.downloadBtn.on('click', this.onDownload, this);
28256 this.trashBtn.on('click', this.onTrash, this);
28258 this.downloadBtn.hide();
28259 this.trashBtn.hide();
28261 if(this.showDownload){
28262 this.downloadBtn.show();
28265 if(this.showTrash){
28266 this.trashBtn.show();
28269 if(!this.showDownload && !this.showTrash) {
28270 this.footerEl.hide();
28275 initial : function()
28277 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
28280 this.fireEvent('initial', this);
28284 onClick : function(e)
28286 e.preventDefault();
28288 this.fireEvent('click', this);
28291 onDownload : function(e)
28293 e.preventDefault();
28295 this.fireEvent('download', this);
28298 onTrash : function(e)
28300 e.preventDefault();
28302 this.fireEvent('trash', this);
28314 * @class Roo.bootstrap.NavProgressBar
28315 * @extends Roo.bootstrap.Component
28316 * Bootstrap NavProgressBar class
28319 * Create a new nav progress bar
28320 * @param {Object} config The config object
28323 Roo.bootstrap.NavProgressBar = function(config){
28324 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28326 this.bullets = this.bullets || [];
28328 // Roo.bootstrap.NavProgressBar.register(this);
28332 * Fires when the active item changes
28333 * @param {Roo.bootstrap.NavProgressBar} this
28334 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28335 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
28342 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
28347 getAutoCreate : function()
28349 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28353 cls : 'roo-navigation-bar-group',
28357 cls : 'roo-navigation-top-bar'
28361 cls : 'roo-navigation-bullets-bar',
28365 cls : 'roo-navigation-bar'
28372 cls : 'roo-navigation-bottom-bar'
28382 initEvents: function()
28387 onRender : function(ct, position)
28389 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28391 if(this.bullets.length){
28392 Roo.each(this.bullets, function(b){
28401 addItem : function(cfg)
28403 var item = new Roo.bootstrap.NavProgressItem(cfg);
28405 item.parentId = this.id;
28406 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28409 var top = new Roo.bootstrap.Element({
28411 cls : 'roo-navigation-bar-text'
28414 var bottom = new Roo.bootstrap.Element({
28416 cls : 'roo-navigation-bar-text'
28419 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28420 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28422 var topText = new Roo.bootstrap.Element({
28424 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28427 var bottomText = new Roo.bootstrap.Element({
28429 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28432 topText.onRender(top.el, null);
28433 bottomText.onRender(bottom.el, null);
28436 item.bottomEl = bottom;
28439 this.barItems.push(item);
28444 getActive : function()
28446 var active = false;
28448 Roo.each(this.barItems, function(v){
28450 if (!v.isActive()) {
28462 setActiveItem : function(item)
28466 Roo.each(this.barItems, function(v){
28467 if (v.rid == item.rid) {
28471 if (v.isActive()) {
28472 v.setActive(false);
28477 item.setActive(true);
28479 this.fireEvent('changed', this, item, prev);
28482 getBarItem: function(rid)
28486 Roo.each(this.barItems, function(e) {
28487 if (e.rid != rid) {
28498 indexOfItem : function(item)
28502 Roo.each(this.barItems, function(v, i){
28504 if (v.rid != item.rid) {
28515 setActiveNext : function()
28517 var i = this.indexOfItem(this.getActive());
28519 if (i > this.barItems.length) {
28523 this.setActiveItem(this.barItems[i+1]);
28526 setActivePrev : function()
28528 var i = this.indexOfItem(this.getActive());
28534 this.setActiveItem(this.barItems[i-1]);
28537 format : function()
28539 if(!this.barItems.length){
28543 var width = 100 / this.barItems.length;
28545 Roo.each(this.barItems, function(i){
28546 i.el.setStyle('width', width + '%');
28547 i.topEl.el.setStyle('width', width + '%');
28548 i.bottomEl.el.setStyle('width', width + '%');
28557 * Nav Progress Item
28562 * @class Roo.bootstrap.NavProgressItem
28563 * @extends Roo.bootstrap.Component
28564 * Bootstrap NavProgressItem class
28565 * @cfg {String} rid the reference id
28566 * @cfg {Boolean} active (true|false) Is item active default false
28567 * @cfg {Boolean} disabled (true|false) Is item active default false
28568 * @cfg {String} html
28569 * @cfg {String} position (top|bottom) text position default bottom
28570 * @cfg {String} icon show icon instead of number
28573 * Create a new NavProgressItem
28574 * @param {Object} config The config object
28576 Roo.bootstrap.NavProgressItem = function(config){
28577 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28582 * The raw click event for the entire grid.
28583 * @param {Roo.bootstrap.NavProgressItem} this
28584 * @param {Roo.EventObject} e
28591 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
28597 position : 'bottom',
28600 getAutoCreate : function()
28602 var iconCls = 'roo-navigation-bar-item-icon';
28604 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28608 cls: 'roo-navigation-bar-item',
28618 cfg.cls += ' active';
28621 cfg.cls += ' disabled';
28627 disable : function()
28629 this.setDisabled(true);
28632 enable : function()
28634 this.setDisabled(false);
28637 initEvents: function()
28639 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28641 this.iconEl.on('click', this.onClick, this);
28644 onClick : function(e)
28646 e.preventDefault();
28652 if(this.fireEvent('click', this, e) === false){
28656 this.parent().setActiveItem(this);
28659 isActive: function ()
28661 return this.active;
28664 setActive : function(state)
28666 if(this.active == state){
28670 this.active = state;
28673 this.el.addClass('active');
28677 this.el.removeClass('active');
28682 setDisabled : function(state)
28684 if(this.disabled == state){
28688 this.disabled = state;
28691 this.el.addClass('disabled');
28695 this.el.removeClass('disabled');
28698 tooltipEl : function()
28700 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28713 * @class Roo.bootstrap.FieldLabel
28714 * @extends Roo.bootstrap.Component
28715 * Bootstrap FieldLabel class
28716 * @cfg {String} html contents of the element
28717 * @cfg {String} tag tag of the element default label
28718 * @cfg {String} cls class of the element
28719 * @cfg {String} target label target
28720 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28721 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28722 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28723 * @cfg {String} iconTooltip default "This field is required"
28726 * Create a new FieldLabel
28727 * @param {Object} config The config object
28730 Roo.bootstrap.FieldLabel = function(config){
28731 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28736 * Fires after the field has been marked as invalid.
28737 * @param {Roo.form.FieldLabel} this
28738 * @param {String} msg The validation message
28743 * Fires after the field has been validated with no errors.
28744 * @param {Roo.form.FieldLabel} this
28750 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28757 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28758 validClass : 'text-success fa fa-lg fa-check',
28759 iconTooltip : 'This field is required',
28761 getAutoCreate : function(){
28765 cls : 'roo-bootstrap-field-label ' + this.cls,
28771 tooltip : this.iconTooltip
28783 initEvents: function()
28785 Roo.bootstrap.Element.superclass.initEvents.call(this);
28787 this.iconEl = this.el.select('i', true).first();
28789 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28791 Roo.bootstrap.FieldLabel.register(this);
28795 * Mark this field as valid
28797 markValid : function()
28799 this.iconEl.show();
28801 this.iconEl.removeClass(this.invalidClass);
28803 this.iconEl.addClass(this.validClass);
28805 this.fireEvent('valid', this);
28809 * Mark this field as invalid
28810 * @param {String} msg The validation message
28812 markInvalid : function(msg)
28814 this.iconEl.show();
28816 this.iconEl.removeClass(this.validClass);
28818 this.iconEl.addClass(this.invalidClass);
28820 this.fireEvent('invalid', this, msg);
28826 Roo.apply(Roo.bootstrap.FieldLabel, {
28831 * register a FieldLabel Group
28832 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28834 register : function(label)
28836 if(this.groups.hasOwnProperty(label.target)){
28840 this.groups[label.target] = label;
28844 * fetch a FieldLabel Group based on the target
28845 * @param {string} target
28846 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28848 get: function(target) {
28849 if (typeof(this.groups[target]) == 'undefined') {
28853 return this.groups[target] ;
28862 * page DateSplitField.
28868 * @class Roo.bootstrap.DateSplitField
28869 * @extends Roo.bootstrap.Component
28870 * Bootstrap DateSplitField class
28871 * @cfg {string} fieldLabel - the label associated
28872 * @cfg {Number} labelWidth set the width of label (0-12)
28873 * @cfg {String} labelAlign (top|left)
28874 * @cfg {Boolean} dayAllowBlank (true|false) default false
28875 * @cfg {Boolean} monthAllowBlank (true|false) default false
28876 * @cfg {Boolean} yearAllowBlank (true|false) default false
28877 * @cfg {string} dayPlaceholder
28878 * @cfg {string} monthPlaceholder
28879 * @cfg {string} yearPlaceholder
28880 * @cfg {string} dayFormat default 'd'
28881 * @cfg {string} monthFormat default 'm'
28882 * @cfg {string} yearFormat default 'Y'
28886 * Create a new DateSplitField
28887 * @param {Object} config The config object
28890 Roo.bootstrap.DateSplitField = function(config){
28891 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28897 * getting the data of years
28898 * @param {Roo.bootstrap.DateSplitField} this
28899 * @param {Object} years
28904 * getting the data of days
28905 * @param {Roo.bootstrap.DateSplitField} this
28906 * @param {Object} days
28911 * Fires after the field has been marked as invalid.
28912 * @param {Roo.form.Field} this
28913 * @param {String} msg The validation message
28918 * Fires after the field has been validated with no errors.
28919 * @param {Roo.form.Field} this
28925 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28928 labelAlign : 'top',
28930 dayAllowBlank : false,
28931 monthAllowBlank : false,
28932 yearAllowBlank : false,
28933 dayPlaceholder : '',
28934 monthPlaceholder : '',
28935 yearPlaceholder : '',
28939 isFormField : true,
28941 getAutoCreate : function()
28945 cls : 'row roo-date-split-field-group',
28950 cls : 'form-hidden-field roo-date-split-field-group-value',
28956 if(this.fieldLabel){
28959 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28963 html : this.fieldLabel
28969 Roo.each(['day', 'month', 'year'], function(t){
28972 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28979 inputEl: function ()
28981 return this.el.select('.roo-date-split-field-group-value', true).first();
28984 onRender : function(ct, position)
28988 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28990 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28992 this.dayField = new Roo.bootstrap.ComboBox({
28993 allowBlank : this.dayAllowBlank,
28994 alwaysQuery : true,
28995 displayField : 'value',
28998 forceSelection : true,
29000 placeholder : this.dayPlaceholder,
29001 selectOnFocus : true,
29002 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29003 triggerAction : 'all',
29005 valueField : 'value',
29006 store : new Roo.data.SimpleStore({
29007 data : (function() {
29009 _this.fireEvent('days', _this, days);
29012 fields : [ 'value' ]
29015 select : function (_self, record, index)
29017 _this.setValue(_this.getValue());
29022 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29024 this.monthField = new Roo.bootstrap.MonthField({
29025 after : '<i class=\"fa fa-calendar\"></i>',
29026 allowBlank : this.monthAllowBlank,
29027 placeholder : this.monthPlaceholder,
29030 render : function (_self)
29032 this.el.select('span.input-group-addon', true).first().on('click', function(e){
29033 e.preventDefault();
29037 select : function (_self, oldvalue, newvalue)
29039 _this.setValue(_this.getValue());
29044 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29046 this.yearField = new Roo.bootstrap.ComboBox({
29047 allowBlank : this.yearAllowBlank,
29048 alwaysQuery : true,
29049 displayField : 'value',
29052 forceSelection : true,
29054 placeholder : this.yearPlaceholder,
29055 selectOnFocus : true,
29056 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29057 triggerAction : 'all',
29059 valueField : 'value',
29060 store : new Roo.data.SimpleStore({
29061 data : (function() {
29063 _this.fireEvent('years', _this, years);
29066 fields : [ 'value' ]
29069 select : function (_self, record, index)
29071 _this.setValue(_this.getValue());
29076 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29079 setValue : function(v, format)
29081 this.inputEl.dom.value = v;
29083 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29085 var d = Date.parseDate(v, f);
29092 this.setDay(d.format(this.dayFormat));
29093 this.setMonth(d.format(this.monthFormat));
29094 this.setYear(d.format(this.yearFormat));
29101 setDay : function(v)
29103 this.dayField.setValue(v);
29104 this.inputEl.dom.value = this.getValue();
29109 setMonth : function(v)
29111 this.monthField.setValue(v, true);
29112 this.inputEl.dom.value = this.getValue();
29117 setYear : function(v)
29119 this.yearField.setValue(v);
29120 this.inputEl.dom.value = this.getValue();
29125 getDay : function()
29127 return this.dayField.getValue();
29130 getMonth : function()
29132 return this.monthField.getValue();
29135 getYear : function()
29137 return this.yearField.getValue();
29140 getValue : function()
29142 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29144 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29154 this.inputEl.dom.value = '';
29159 validate : function()
29161 var d = this.dayField.validate();
29162 var m = this.monthField.validate();
29163 var y = this.yearField.validate();
29168 (!this.dayAllowBlank && !d) ||
29169 (!this.monthAllowBlank && !m) ||
29170 (!this.yearAllowBlank && !y)
29175 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29184 this.markInvalid();
29189 markValid : function()
29192 var label = this.el.select('label', true).first();
29193 var icon = this.el.select('i.fa-star', true).first();
29199 this.fireEvent('valid', this);
29203 * Mark this field as invalid
29204 * @param {String} msg The validation message
29206 markInvalid : function(msg)
29209 var label = this.el.select('label', true).first();
29210 var icon = this.el.select('i.fa-star', true).first();
29212 if(label && !icon){
29213 this.el.select('.roo-date-split-field-label', true).createChild({
29215 cls : 'text-danger fa fa-lg fa-star',
29216 tooltip : 'This field is required',
29217 style : 'margin-right:5px;'
29221 this.fireEvent('invalid', this, msg);
29224 clearInvalid : function()
29226 var label = this.el.select('label', true).first();
29227 var icon = this.el.select('i.fa-star', true).first();
29233 this.fireEvent('valid', this);
29236 getName: function()
29246 * http://masonry.desandro.com
29248 * The idea is to render all the bricks based on vertical width...
29250 * The original code extends 'outlayer' - we might need to use that....
29256 * @class Roo.bootstrap.LayoutMasonry
29257 * @extends Roo.bootstrap.Component
29258 * Bootstrap Layout Masonry class
29261 * Create a new Element
29262 * @param {Object} config The config object
29265 Roo.bootstrap.LayoutMasonry = function(config){
29266 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29272 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
29275 * @cfg {Boolean} isLayoutInstant = no animation?
29277 isLayoutInstant : false, // needed?
29280 * @cfg {Number} boxWidth width of the columns
29285 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
29290 * @cfg {Number} padWidth padding below box..
29295 * @cfg {Number} gutter gutter width..
29300 * @cfg {Number} maxCols maximum number of columns
29306 * @cfg {Boolean} isAutoInitial defalut true
29308 isAutoInitial : true,
29313 * @cfg {Boolean} isHorizontal defalut false
29315 isHorizontal : false,
29317 currentSize : null,
29323 bricks: null, //CompositeElement
29327 _isLayoutInited : false,
29329 // isAlternative : false, // only use for vertical layout...
29332 * @cfg {Number} alternativePadWidth padding below box..
29334 alternativePadWidth : 50,
29336 getAutoCreate : function(){
29340 cls: 'blog-masonary-wrapper ' + this.cls,
29342 cls : 'mas-boxes masonary'
29349 getChildContainer: function( )
29351 if (this.boxesEl) {
29352 return this.boxesEl;
29355 this.boxesEl = this.el.select('.mas-boxes').first();
29357 return this.boxesEl;
29361 initEvents : function()
29365 if(this.isAutoInitial){
29366 Roo.log('hook children rendered');
29367 this.on('childrenrendered', function() {
29368 Roo.log('children rendered');
29374 initial : function()
29376 this.currentSize = this.el.getBox(true);
29378 Roo.EventManager.onWindowResize(this.resize, this);
29380 if(!this.isAutoInitial){
29388 //this.layout.defer(500,this);
29392 resize : function()
29396 var cs = this.el.getBox(true);
29398 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29399 Roo.log("no change in with or X");
29403 this.currentSize = cs;
29409 layout : function()
29411 this._resetLayout();
29413 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29415 this.layoutItems( isInstant );
29417 this._isLayoutInited = true;
29421 _resetLayout : function()
29423 if(this.isHorizontal){
29424 this.horizontalMeasureColumns();
29428 this.verticalMeasureColumns();
29432 verticalMeasureColumns : function()
29434 this.getContainerWidth();
29436 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29437 // this.colWidth = Math.floor(this.containerWidth * 0.8);
29441 var boxWidth = this.boxWidth + this.padWidth;
29443 if(this.containerWidth < this.boxWidth){
29444 boxWidth = this.containerWidth
29447 var containerWidth = this.containerWidth;
29449 var cols = Math.floor(containerWidth / boxWidth);
29451 this.cols = Math.max( cols, 1 );
29453 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29455 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29457 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29459 this.colWidth = boxWidth + avail - this.padWidth;
29461 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29462 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
29465 horizontalMeasureColumns : function()
29467 this.getContainerWidth();
29469 var boxWidth = this.boxWidth;
29471 if(this.containerWidth < boxWidth){
29472 boxWidth = this.containerWidth;
29475 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29477 this.el.setHeight(boxWidth);
29481 getContainerWidth : function()
29483 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
29486 layoutItems : function( isInstant )
29488 var items = Roo.apply([], this.bricks);
29490 if(this.isHorizontal){
29491 this._horizontalLayoutItems( items , isInstant );
29495 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29496 // this._verticalAlternativeLayoutItems( items , isInstant );
29500 this._verticalLayoutItems( items , isInstant );
29504 _verticalLayoutItems : function ( items , isInstant)
29506 if ( !items || !items.length ) {
29511 ['xs', 'xs', 'xs', 'tall'],
29512 ['xs', 'xs', 'tall'],
29513 ['xs', 'xs', 'sm'],
29514 ['xs', 'xs', 'xs'],
29520 ['sm', 'xs', 'xs'],
29524 ['tall', 'xs', 'xs', 'xs'],
29525 ['tall', 'xs', 'xs'],
29537 Roo.each(items, function(item, k){
29539 switch (item.size) {
29540 // these layouts take up a full box,
29551 boxes.push([item]);
29574 var filterPattern = function(box, length)
29582 var pattern = box.slice(0, length);
29586 Roo.each(pattern, function(i){
29587 format.push(i.size);
29590 Roo.each(standard, function(s){
29592 if(String(s) != String(format)){
29601 if(!match && length == 1){
29606 filterPattern(box, length - 1);
29610 queue.push(pattern);
29612 box = box.slice(length, box.length);
29614 filterPattern(box, 4);
29620 Roo.each(boxes, function(box, k){
29626 if(box.length == 1){
29631 filterPattern(box, 4);
29635 this._processVerticalLayoutQueue( queue, isInstant );
29639 // _verticalAlternativeLayoutItems : function( items , isInstant )
29641 // if ( !items || !items.length ) {
29645 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
29649 _horizontalLayoutItems : function ( items , isInstant)
29651 if ( !items || !items.length || items.length < 3) {
29657 var eItems = items.slice(0, 3);
29659 items = items.slice(3, items.length);
29662 ['xs', 'xs', 'xs', 'wide'],
29663 ['xs', 'xs', 'wide'],
29664 ['xs', 'xs', 'sm'],
29665 ['xs', 'xs', 'xs'],
29671 ['sm', 'xs', 'xs'],
29675 ['wide', 'xs', 'xs', 'xs'],
29676 ['wide', 'xs', 'xs'],
29689 Roo.each(items, function(item, k){
29691 switch (item.size) {
29702 boxes.push([item]);
29726 var filterPattern = function(box, length)
29734 var pattern = box.slice(0, length);
29738 Roo.each(pattern, function(i){
29739 format.push(i.size);
29742 Roo.each(standard, function(s){
29744 if(String(s) != String(format)){
29753 if(!match && length == 1){
29758 filterPattern(box, length - 1);
29762 queue.push(pattern);
29764 box = box.slice(length, box.length);
29766 filterPattern(box, 4);
29772 Roo.each(boxes, function(box, k){
29778 if(box.length == 1){
29783 filterPattern(box, 4);
29790 var pos = this.el.getBox(true);
29794 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29796 var hit_end = false;
29798 Roo.each(queue, function(box){
29802 Roo.each(box, function(b){
29804 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29814 Roo.each(box, function(b){
29816 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29819 mx = Math.max(mx, b.x);
29823 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29827 Roo.each(box, function(b){
29829 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29843 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29846 /** Sets position of item in DOM
29847 * @param {Element} item
29848 * @param {Number} x - horizontal position
29849 * @param {Number} y - vertical position
29850 * @param {Boolean} isInstant - disables transitions
29852 _processVerticalLayoutQueue : function( queue, isInstant )
29854 var pos = this.el.getBox(true);
29859 for (var i = 0; i < this.cols; i++){
29863 Roo.each(queue, function(box, k){
29865 var col = k % this.cols;
29867 Roo.each(box, function(b,kk){
29869 b.el.position('absolute');
29871 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29872 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29874 if(b.size == 'md-left' || b.size == 'md-right'){
29875 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29876 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29879 b.el.setWidth(width);
29880 b.el.setHeight(height);
29882 b.el.select('iframe',true).setSize(width,height);
29886 for (var i = 0; i < this.cols; i++){
29888 if(maxY[i] < maxY[col]){
29893 col = Math.min(col, i);
29897 x = pos.x + col * (this.colWidth + this.padWidth);
29901 var positions = [];
29903 switch (box.length){
29905 positions = this.getVerticalOneBoxColPositions(x, y, box);
29908 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29911 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29914 positions = this.getVerticalFourBoxColPositions(x, y, box);
29920 Roo.each(box, function(b,kk){
29922 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29924 var sz = b.el.getSize();
29926 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29934 for (var i = 0; i < this.cols; i++){
29935 mY = Math.max(mY, maxY[i]);
29938 this.el.setHeight(mY - pos.y);
29942 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29944 // var pos = this.el.getBox(true);
29947 // var maxX = pos.right;
29949 // var maxHeight = 0;
29951 // Roo.each(items, function(item, k){
29955 // item.el.position('absolute');
29957 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29959 // item.el.setWidth(width);
29961 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29963 // item.el.setHeight(height);
29966 // item.el.setXY([x, y], isInstant ? false : true);
29968 // item.el.setXY([maxX - width, y], isInstant ? false : true);
29971 // y = y + height + this.alternativePadWidth;
29973 // maxHeight = maxHeight + height + this.alternativePadWidth;
29977 // this.el.setHeight(maxHeight);
29981 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29983 var pos = this.el.getBox(true);
29988 var maxX = pos.right;
29990 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29992 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29994 Roo.each(queue, function(box, k){
29996 Roo.each(box, function(b, kk){
29998 b.el.position('absolute');
30000 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30001 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30003 if(b.size == 'md-left' || b.size == 'md-right'){
30004 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30005 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30008 b.el.setWidth(width);
30009 b.el.setHeight(height);
30017 var positions = [];
30019 switch (box.length){
30021 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30024 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30027 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30030 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30036 Roo.each(box, function(b,kk){
30038 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30040 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30048 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30050 Roo.each(eItems, function(b,k){
30052 b.size = (k == 0) ? 'sm' : 'xs';
30053 b.x = (k == 0) ? 2 : 1;
30054 b.y = (k == 0) ? 2 : 1;
30056 b.el.position('absolute');
30058 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30060 b.el.setWidth(width);
30062 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30064 b.el.setHeight(height);
30068 var positions = [];
30071 x : maxX - this.unitWidth * 2 - this.gutter,
30076 x : maxX - this.unitWidth,
30077 y : minY + (this.unitWidth + this.gutter) * 2
30081 x : maxX - this.unitWidth * 3 - this.gutter * 2,
30085 Roo.each(eItems, function(b,k){
30087 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30093 getVerticalOneBoxColPositions : function(x, y, box)
30097 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30099 if(box[0].size == 'md-left'){
30103 if(box[0].size == 'md-right'){
30108 x : x + (this.unitWidth + this.gutter) * rand,
30115 getVerticalTwoBoxColPositions : function(x, y, box)
30119 if(box[0].size == 'xs'){
30123 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30127 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30141 x : x + (this.unitWidth + this.gutter) * 2,
30142 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30149 getVerticalThreeBoxColPositions : function(x, y, box)
30153 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30161 x : x + (this.unitWidth + this.gutter) * 1,
30166 x : x + (this.unitWidth + this.gutter) * 2,
30174 if(box[0].size == 'xs' && box[1].size == 'xs'){
30183 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30187 x : x + (this.unitWidth + this.gutter) * 1,
30201 x : x + (this.unitWidth + this.gutter) * 2,
30206 x : x + (this.unitWidth + this.gutter) * 2,
30207 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30214 getVerticalFourBoxColPositions : function(x, y, box)
30218 if(box[0].size == 'xs'){
30227 y : y + (this.unitHeight + this.gutter) * 1
30232 y : y + (this.unitHeight + this.gutter) * 2
30236 x : x + (this.unitWidth + this.gutter) * 1,
30250 x : x + (this.unitWidth + this.gutter) * 2,
30255 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30256 y : y + (this.unitHeight + this.gutter) * 1
30260 x : x + (this.unitWidth + this.gutter) * 2,
30261 y : y + (this.unitWidth + this.gutter) * 2
30268 getHorizontalOneBoxColPositions : function(maxX, minY, box)
30272 if(box[0].size == 'md-left'){
30274 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30281 if(box[0].size == 'md-right'){
30283 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30284 y : minY + (this.unitWidth + this.gutter) * 1
30290 var rand = Math.floor(Math.random() * (4 - box[0].y));
30293 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30294 y : minY + (this.unitWidth + this.gutter) * rand
30301 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30305 if(box[0].size == 'xs'){
30308 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30313 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30314 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30322 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30327 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30328 y : minY + (this.unitWidth + this.gutter) * 2
30335 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30339 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30342 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30347 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30348 y : minY + (this.unitWidth + this.gutter) * 1
30352 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30353 y : minY + (this.unitWidth + this.gutter) * 2
30360 if(box[0].size == 'xs' && box[1].size == 'xs'){
30363 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30368 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30373 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30374 y : minY + (this.unitWidth + this.gutter) * 1
30382 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30387 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30388 y : minY + (this.unitWidth + this.gutter) * 2
30392 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30393 y : minY + (this.unitWidth + this.gutter) * 2
30400 getHorizontalFourBoxColPositions : function(maxX, minY, box)
30404 if(box[0].size == 'xs'){
30407 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30412 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30417 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),
30422 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30423 y : minY + (this.unitWidth + this.gutter) * 1
30431 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30436 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30437 y : minY + (this.unitWidth + this.gutter) * 2
30441 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30442 y : minY + (this.unitWidth + this.gutter) * 2
30446 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),
30447 y : minY + (this.unitWidth + this.gutter) * 2
30461 * http://masonry.desandro.com
30463 * The idea is to render all the bricks based on vertical width...
30465 * The original code extends 'outlayer' - we might need to use that....
30471 * @class Roo.bootstrap.LayoutMasonryAuto
30472 * @extends Roo.bootstrap.Component
30473 * Bootstrap Layout Masonry class
30476 * Create a new Element
30477 * @param {Object} config The config object
30480 Roo.bootstrap.LayoutMasonryAuto = function(config){
30481 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30484 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
30487 * @cfg {Boolean} isFitWidth - resize the width..
30489 isFitWidth : false, // options..
30491 * @cfg {Boolean} isOriginLeft = left align?
30493 isOriginLeft : true,
30495 * @cfg {Boolean} isOriginTop = top align?
30497 isOriginTop : false,
30499 * @cfg {Boolean} isLayoutInstant = no animation?
30501 isLayoutInstant : false, // needed?
30503 * @cfg {Boolean} isResizingContainer = not sure if this is used..
30505 isResizingContainer : true,
30507 * @cfg {Number} columnWidth width of the columns
30513 * @cfg {Number} maxCols maximum number of columns
30518 * @cfg {Number} padHeight padding below box..
30524 * @cfg {Boolean} isAutoInitial defalut true
30527 isAutoInitial : true,
30533 initialColumnWidth : 0,
30534 currentSize : null,
30536 colYs : null, // array.
30543 bricks: null, //CompositeElement
30544 cols : 0, // array?
30545 // element : null, // wrapped now this.el
30546 _isLayoutInited : null,
30549 getAutoCreate : function(){
30553 cls: 'blog-masonary-wrapper ' + this.cls,
30555 cls : 'mas-boxes masonary'
30562 getChildContainer: function( )
30564 if (this.boxesEl) {
30565 return this.boxesEl;
30568 this.boxesEl = this.el.select('.mas-boxes').first();
30570 return this.boxesEl;
30574 initEvents : function()
30578 if(this.isAutoInitial){
30579 Roo.log('hook children rendered');
30580 this.on('childrenrendered', function() {
30581 Roo.log('children rendered');
30588 initial : function()
30590 this.reloadItems();
30592 this.currentSize = this.el.getBox(true);
30594 /// was window resize... - let's see if this works..
30595 Roo.EventManager.onWindowResize(this.resize, this);
30597 if(!this.isAutoInitial){
30602 this.layout.defer(500,this);
30605 reloadItems: function()
30607 this.bricks = this.el.select('.masonry-brick', true);
30609 this.bricks.each(function(b) {
30610 //Roo.log(b.getSize());
30611 if (!b.attr('originalwidth')) {
30612 b.attr('originalwidth', b.getSize().width);
30617 Roo.log(this.bricks.elements.length);
30620 resize : function()
30623 var cs = this.el.getBox(true);
30625 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30626 Roo.log("no change in with or X");
30629 this.currentSize = cs;
30633 layout : function()
30636 this._resetLayout();
30637 //this._manageStamps();
30639 // don't animate first layout
30640 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30641 this.layoutItems( isInstant );
30643 // flag for initalized
30644 this._isLayoutInited = true;
30647 layoutItems : function( isInstant )
30649 //var items = this._getItemsForLayout( this.items );
30650 // original code supports filtering layout items.. we just ignore it..
30652 this._layoutItems( this.bricks , isInstant );
30654 this._postLayout();
30656 _layoutItems : function ( items , isInstant)
30658 //this.fireEvent( 'layout', this, items );
30661 if ( !items || !items.elements.length ) {
30662 // no items, emit event with empty array
30667 items.each(function(item) {
30668 Roo.log("layout item");
30670 // get x/y object from method
30671 var position = this._getItemLayoutPosition( item );
30673 position.item = item;
30674 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30675 queue.push( position );
30678 this._processLayoutQueue( queue );
30680 /** Sets position of item in DOM
30681 * @param {Element} item
30682 * @param {Number} x - horizontal position
30683 * @param {Number} y - vertical position
30684 * @param {Boolean} isInstant - disables transitions
30686 _processLayoutQueue : function( queue )
30688 for ( var i=0, len = queue.length; i < len; i++ ) {
30689 var obj = queue[i];
30690 obj.item.position('absolute');
30691 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30697 * Any logic you want to do after each layout,
30698 * i.e. size the container
30700 _postLayout : function()
30702 this.resizeContainer();
30705 resizeContainer : function()
30707 if ( !this.isResizingContainer ) {
30710 var size = this._getContainerSize();
30712 this.el.setSize(size.width,size.height);
30713 this.boxesEl.setSize(size.width,size.height);
30719 _resetLayout : function()
30721 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30722 this.colWidth = this.el.getWidth();
30723 //this.gutter = this.el.getWidth();
30725 this.measureColumns();
30731 this.colYs.push( 0 );
30737 measureColumns : function()
30739 this.getContainerWidth();
30740 // if columnWidth is 0, default to outerWidth of first item
30741 if ( !this.columnWidth ) {
30742 var firstItem = this.bricks.first();
30743 Roo.log(firstItem);
30744 this.columnWidth = this.containerWidth;
30745 if (firstItem && firstItem.attr('originalwidth') ) {
30746 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30748 // columnWidth fall back to item of first element
30749 Roo.log("set column width?");
30750 this.initialColumnWidth = this.columnWidth ;
30752 // if first elem has no width, default to size of container
30757 if (this.initialColumnWidth) {
30758 this.columnWidth = this.initialColumnWidth;
30763 // column width is fixed at the top - however if container width get's smaller we should
30766 // this bit calcs how man columns..
30768 var columnWidth = this.columnWidth += this.gutter;
30770 // calculate columns
30771 var containerWidth = this.containerWidth + this.gutter;
30773 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30774 // fix rounding errors, typically with gutters
30775 var excess = columnWidth - containerWidth % columnWidth;
30778 // if overshoot is less than a pixel, round up, otherwise floor it
30779 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30780 cols = Math[ mathMethod ]( cols );
30781 this.cols = Math.max( cols, 1 );
30782 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30784 // padding positioning..
30785 var totalColWidth = this.cols * this.columnWidth;
30786 var padavail = this.containerWidth - totalColWidth;
30787 // so for 2 columns - we need 3 'pads'
30789 var padNeeded = (1+this.cols) * this.padWidth;
30791 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30793 this.columnWidth += padExtra
30794 //this.padWidth = Math.floor(padavail / ( this.cols));
30796 // adjust colum width so that padding is fixed??
30798 // we have 3 columns ... total = width * 3
30799 // we have X left over... that should be used by
30801 //if (this.expandC) {
30809 getContainerWidth : function()
30811 /* // container is parent if fit width
30812 var container = this.isFitWidth ? this.element.parentNode : this.element;
30813 // check that this.size and size are there
30814 // IE8 triggers resize on body size change, so they might not be
30816 var size = getSize( container ); //FIXME
30817 this.containerWidth = size && size.innerWidth; //FIXME
30820 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30824 _getItemLayoutPosition : function( item ) // what is item?
30826 // we resize the item to our columnWidth..
30828 item.setWidth(this.columnWidth);
30829 item.autoBoxAdjust = false;
30831 var sz = item.getSize();
30833 // how many columns does this brick span
30834 var remainder = this.containerWidth % this.columnWidth;
30836 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30837 // round if off by 1 pixel, otherwise use ceil
30838 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30839 colSpan = Math.min( colSpan, this.cols );
30841 // normally this should be '1' as we dont' currently allow multi width columns..
30843 var colGroup = this._getColGroup( colSpan );
30844 // get the minimum Y value from the columns
30845 var minimumY = Math.min.apply( Math, colGroup );
30846 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30848 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30850 // position the brick
30852 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30853 y: this.currentSize.y + minimumY + this.padHeight
30857 // apply setHeight to necessary columns
30858 var setHeight = minimumY + sz.height + this.padHeight;
30859 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30861 var setSpan = this.cols + 1 - colGroup.length;
30862 for ( var i = 0; i < setSpan; i++ ) {
30863 this.colYs[ shortColIndex + i ] = setHeight ;
30870 * @param {Number} colSpan - number of columns the element spans
30871 * @returns {Array} colGroup
30873 _getColGroup : function( colSpan )
30875 if ( colSpan < 2 ) {
30876 // if brick spans only one column, use all the column Ys
30881 // how many different places could this brick fit horizontally
30882 var groupCount = this.cols + 1 - colSpan;
30883 // for each group potential horizontal position
30884 for ( var i = 0; i < groupCount; i++ ) {
30885 // make an array of colY values for that one group
30886 var groupColYs = this.colYs.slice( i, i + colSpan );
30887 // and get the max value of the array
30888 colGroup[i] = Math.max.apply( Math, groupColYs );
30893 _manageStamp : function( stamp )
30895 var stampSize = stamp.getSize();
30896 var offset = stamp.getBox();
30897 // get the columns that this stamp affects
30898 var firstX = this.isOriginLeft ? offset.x : offset.right;
30899 var lastX = firstX + stampSize.width;
30900 var firstCol = Math.floor( firstX / this.columnWidth );
30901 firstCol = Math.max( 0, firstCol );
30903 var lastCol = Math.floor( lastX / this.columnWidth );
30904 // lastCol should not go over if multiple of columnWidth #425
30905 lastCol -= lastX % this.columnWidth ? 0 : 1;
30906 lastCol = Math.min( this.cols - 1, lastCol );
30908 // set colYs to bottom of the stamp
30909 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30912 for ( var i = firstCol; i <= lastCol; i++ ) {
30913 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30918 _getContainerSize : function()
30920 this.maxY = Math.max.apply( Math, this.colYs );
30925 if ( this.isFitWidth ) {
30926 size.width = this._getContainerFitWidth();
30932 _getContainerFitWidth : function()
30934 var unusedCols = 0;
30935 // count unused columns
30938 if ( this.colYs[i] !== 0 ) {
30943 // fit container to columns that have been used
30944 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30947 needsResizeLayout : function()
30949 var previousWidth = this.containerWidth;
30950 this.getContainerWidth();
30951 return previousWidth !== this.containerWidth;
30966 * @class Roo.bootstrap.MasonryBrick
30967 * @extends Roo.bootstrap.Component
30968 * Bootstrap MasonryBrick class
30971 * Create a new MasonryBrick
30972 * @param {Object} config The config object
30975 Roo.bootstrap.MasonryBrick = function(config){
30976 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30982 * When a MasonryBrick is clcik
30983 * @param {Roo.bootstrap.MasonryBrick} this
30984 * @param {Roo.EventObject} e
30990 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
30993 * @cfg {String} title
30997 * @cfg {String} html
31001 * @cfg {String} bgimage
31005 * @cfg {String} videourl
31009 * @cfg {String} cls
31013 * @cfg {String} href
31017 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31022 * @cfg {String} (center|bottom) placetitle
31027 * @cfg {Boolean} isFitContainer defalut true
31029 isFitContainer : true,
31032 * @cfg {Boolean} preventDefault defalut false
31034 preventDefault : false,
31036 getAutoCreate : function()
31038 if(!this.isFitContainer){
31039 return this.getSplitAutoCreate();
31042 var cls = 'masonry-brick masonry-brick-full';
31044 if(this.href.length){
31045 cls += ' masonry-brick-link';
31048 if(this.bgimage.length){
31049 cls += ' masonry-brick-image';
31052 if(!this.html.length){
31053 cls += ' enable-mask';
31057 cls += ' masonry-' + this.size + '-brick';
31060 if(this.placetitle.length){
31062 switch (this.placetitle) {
31064 cls += ' masonry-center-title';
31067 cls += ' masonry-bottom-title';
31074 if(!this.html.length && !this.bgimage.length){
31075 cls += ' masonry-center-title';
31078 if(!this.html.length && this.bgimage.length){
31079 cls += ' masonry-bottom-title';
31084 cls += ' ' + this.cls;
31088 tag: (this.href.length) ? 'a' : 'div',
31093 cls: 'masonry-brick-paragraph',
31099 if(this.href.length){
31100 cfg.href = this.href;
31103 var cn = cfg.cn[0].cn;
31105 if(this.title.length){
31108 cls: 'masonry-brick-title',
31113 if(this.html.length){
31116 cls: 'masonry-brick-text',
31120 if (!this.title.length && !this.html.length) {
31121 cfg.cn[0].cls += ' hide';
31124 if(this.bgimage.length){
31127 cls: 'masonry-brick-image-view',
31132 if(this.videourl.length){
31133 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31134 // youtube support only?
31137 cls: 'masonry-brick-image-view',
31140 allowfullscreen : true
31148 cls: 'masonry-brick-mask'
31155 getSplitAutoCreate : function()
31157 var cls = 'masonry-brick masonry-brick-split';
31159 if(this.href.length){
31160 cls += ' masonry-brick-link';
31163 if(this.bgimage.length){
31164 cls += ' masonry-brick-image';
31168 cls += ' masonry-' + this.size + '-brick';
31171 switch (this.placetitle) {
31173 cls += ' masonry-center-title';
31176 cls += ' masonry-bottom-title';
31179 if(!this.bgimage.length){
31180 cls += ' masonry-center-title';
31183 if(this.bgimage.length){
31184 cls += ' masonry-bottom-title';
31190 cls += ' ' + this.cls;
31194 tag: (this.href.length) ? 'a' : 'div',
31199 cls: 'masonry-brick-split-head',
31203 cls: 'masonry-brick-paragraph',
31210 cls: 'masonry-brick-split-body',
31216 if(this.href.length){
31217 cfg.href = this.href;
31220 if(this.title.length){
31221 cfg.cn[0].cn[0].cn.push({
31223 cls: 'masonry-brick-title',
31228 if(this.html.length){
31229 cfg.cn[1].cn.push({
31231 cls: 'masonry-brick-text',
31236 if(this.bgimage.length){
31237 cfg.cn[0].cn.push({
31239 cls: 'masonry-brick-image-view',
31244 if(this.videourl.length){
31245 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31246 // youtube support only?
31247 cfg.cn[0].cn.cn.push({
31249 cls: 'masonry-brick-image-view',
31252 allowfullscreen : true
31259 initEvents: function()
31261 switch (this.size) {
31294 this.el.on('touchstart', this.onTouchStart, this);
31295 this.el.on('touchmove', this.onTouchMove, this);
31296 this.el.on('touchend', this.onTouchEnd, this);
31297 this.el.on('contextmenu', this.onContextMenu, this);
31299 this.el.on('mouseenter' ,this.enter, this);
31300 this.el.on('mouseleave', this.leave, this);
31301 this.el.on('click', this.onClick, this);
31304 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31305 this.parent().bricks.push(this);
31310 onClick: function(e, el)
31312 var time = this.endTimer - this.startTimer;
31316 e.preventDefault();
31321 if(!this.preventDefault){
31325 e.preventDefault();
31326 this.fireEvent('click', this);
31329 enter: function(e, el)
31331 e.preventDefault();
31333 if(!this.isFitContainer){
31337 if(this.bgimage.length && this.html.length){
31338 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31342 leave: function(e, el)
31344 e.preventDefault();
31346 if(!this.isFitContainer){
31350 if(this.bgimage.length && this.html.length){
31351 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31355 onTouchStart: function(e, el)
31357 // e.preventDefault();
31359 this.touchmoved = false;
31361 if(!this.isFitContainer){
31365 if(!this.bgimage.length || !this.html.length){
31369 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31371 this.timer = new Date().getTime();
31375 onTouchMove: function(e, el)
31377 this.touchmoved = true;
31380 onContextMenu : function(e,el)
31382 e.preventDefault();
31383 e.stopPropagation();
31387 onTouchEnd: function(e, el)
31389 // e.preventDefault();
31391 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31398 if(!this.bgimage.length || !this.html.length){
31400 if(this.href.length){
31401 window.location.href = this.href;
31407 if(!this.isFitContainer){
31411 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31413 window.location.href = this.href;
31428 * @class Roo.bootstrap.Brick
31429 * @extends Roo.bootstrap.Component
31430 * Bootstrap Brick class
31433 * Create a new Brick
31434 * @param {Object} config The config object
31437 Roo.bootstrap.Brick = function(config){
31438 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31444 * When a Brick is click
31445 * @param {Roo.bootstrap.Brick} this
31446 * @param {Roo.EventObject} e
31452 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
31455 * @cfg {String} title
31459 * @cfg {String} html
31463 * @cfg {String} bgimage
31467 * @cfg {String} cls
31471 * @cfg {String} href
31475 * @cfg {String} video
31479 * @cfg {Boolean} square
31483 getAutoCreate : function()
31485 var cls = 'roo-brick';
31487 if(this.href.length){
31488 cls += ' roo-brick-link';
31491 if(this.bgimage.length){
31492 cls += ' roo-brick-image';
31495 if(!this.html.length && !this.bgimage.length){
31496 cls += ' roo-brick-center-title';
31499 if(!this.html.length && this.bgimage.length){
31500 cls += ' roo-brick-bottom-title';
31504 cls += ' ' + this.cls;
31508 tag: (this.href.length) ? 'a' : 'div',
31513 cls: 'roo-brick-paragraph',
31519 if(this.href.length){
31520 cfg.href = this.href;
31523 var cn = cfg.cn[0].cn;
31525 if(this.title.length){
31528 cls: 'roo-brick-title',
31533 if(this.html.length){
31536 cls: 'roo-brick-text',
31543 if(this.bgimage.length){
31546 cls: 'roo-brick-image-view',
31554 initEvents: function()
31556 if(this.title.length || this.html.length){
31557 this.el.on('mouseenter' ,this.enter, this);
31558 this.el.on('mouseleave', this.leave, this);
31562 Roo.EventManager.onWindowResize(this.resize, this);
31567 resize : function()
31569 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31571 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31573 if(this.bgimage.length){
31574 var image = this.el.select('.roo-brick-image-view', true).first();
31575 image.setWidth(paragraph.getWidth());
31576 image.setHeight(paragraph.getWidth());
31578 this.el.setHeight(paragraph.getWidth());
31584 enter: function(e, el)
31586 e.preventDefault();
31588 if(this.bgimage.length){
31589 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31590 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31594 leave: function(e, el)
31596 e.preventDefault();
31598 if(this.bgimage.length){
31599 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31600 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31610 * Ext JS Library 1.1.1
31611 * Copyright(c) 2006-2007, Ext JS, LLC.
31613 * Originally Released Under LGPL - original licence link has changed is not relivant.
31616 * <script type="text/javascript">
31621 * @class Roo.bootstrap.SplitBar
31622 * @extends Roo.util.Observable
31623 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31627 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31628 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31629 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31630 split.minSize = 100;
31631 split.maxSize = 600;
31632 split.animate = true;
31633 split.on('moved', splitterMoved);
31636 * Create a new SplitBar
31637 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
31638 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
31639 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31640 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
31641 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31642 position of the SplitBar).
31644 Roo.bootstrap.SplitBar = function(cfg){
31649 // dragElement : elm
31650 // resizingElement: el,
31652 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31653 // placement : Roo.bootstrap.SplitBar.LEFT ,
31654 // existingProxy ???
31657 this.el = Roo.get(cfg.dragElement, true);
31658 this.el.dom.unselectable = "on";
31660 this.resizingEl = Roo.get(cfg.resizingElement, true);
31664 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31665 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31668 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31671 * The minimum size of the resizing element. (Defaults to 0)
31677 * The maximum size of the resizing element. (Defaults to 2000)
31680 this.maxSize = 2000;
31683 * Whether to animate the transition to the new size
31686 this.animate = false;
31689 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31692 this.useShim = false;
31697 if(!cfg.existingProxy){
31699 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31701 this.proxy = Roo.get(cfg.existingProxy).dom;
31704 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31707 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31710 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31713 this.dragSpecs = {};
31716 * @private The adapter to use to positon and resize elements
31718 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31719 this.adapter.init(this);
31721 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31723 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31724 this.el.addClass("roo-splitbar-h");
31727 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31728 this.el.addClass("roo-splitbar-v");
31734 * Fires when the splitter is moved (alias for {@link #event-moved})
31735 * @param {Roo.bootstrap.SplitBar} this
31736 * @param {Number} newSize the new width or height
31741 * Fires when the splitter is moved
31742 * @param {Roo.bootstrap.SplitBar} this
31743 * @param {Number} newSize the new width or height
31747 * @event beforeresize
31748 * Fires before the splitter is dragged
31749 * @param {Roo.bootstrap.SplitBar} this
31751 "beforeresize" : true,
31753 "beforeapply" : true
31756 Roo.util.Observable.call(this);
31759 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31760 onStartProxyDrag : function(x, y){
31761 this.fireEvent("beforeresize", this);
31763 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
31765 o.enableDisplayMode("block");
31766 // all splitbars share the same overlay
31767 Roo.bootstrap.SplitBar.prototype.overlay = o;
31769 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31770 this.overlay.show();
31771 Roo.get(this.proxy).setDisplayed("block");
31772 var size = this.adapter.getElementSize(this);
31773 this.activeMinSize = this.getMinimumSize();;
31774 this.activeMaxSize = this.getMaximumSize();;
31775 var c1 = size - this.activeMinSize;
31776 var c2 = Math.max(this.activeMaxSize - size, 0);
31777 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31778 this.dd.resetConstraints();
31779 this.dd.setXConstraint(
31780 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
31781 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31783 this.dd.setYConstraint(0, 0);
31785 this.dd.resetConstraints();
31786 this.dd.setXConstraint(0, 0);
31787 this.dd.setYConstraint(
31788 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
31789 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31792 this.dragSpecs.startSize = size;
31793 this.dragSpecs.startPoint = [x, y];
31794 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31798 * @private Called after the drag operation by the DDProxy
31800 onEndProxyDrag : function(e){
31801 Roo.get(this.proxy).setDisplayed(false);
31802 var endPoint = Roo.lib.Event.getXY(e);
31804 this.overlay.hide();
31807 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31808 newSize = this.dragSpecs.startSize +
31809 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31810 endPoint[0] - this.dragSpecs.startPoint[0] :
31811 this.dragSpecs.startPoint[0] - endPoint[0]
31814 newSize = this.dragSpecs.startSize +
31815 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31816 endPoint[1] - this.dragSpecs.startPoint[1] :
31817 this.dragSpecs.startPoint[1] - endPoint[1]
31820 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31821 if(newSize != this.dragSpecs.startSize){
31822 if(this.fireEvent('beforeapply', this, newSize) !== false){
31823 this.adapter.setElementSize(this, newSize);
31824 this.fireEvent("moved", this, newSize);
31825 this.fireEvent("resize", this, newSize);
31831 * Get the adapter this SplitBar uses
31832 * @return The adapter object
31834 getAdapter : function(){
31835 return this.adapter;
31839 * Set the adapter this SplitBar uses
31840 * @param {Object} adapter A SplitBar adapter object
31842 setAdapter : function(adapter){
31843 this.adapter = adapter;
31844 this.adapter.init(this);
31848 * Gets the minimum size for the resizing element
31849 * @return {Number} The minimum size
31851 getMinimumSize : function(){
31852 return this.minSize;
31856 * Sets the minimum size for the resizing element
31857 * @param {Number} minSize The minimum size
31859 setMinimumSize : function(minSize){
31860 this.minSize = minSize;
31864 * Gets the maximum size for the resizing element
31865 * @return {Number} The maximum size
31867 getMaximumSize : function(){
31868 return this.maxSize;
31872 * Sets the maximum size for the resizing element
31873 * @param {Number} maxSize The maximum size
31875 setMaximumSize : function(maxSize){
31876 this.maxSize = maxSize;
31880 * Sets the initialize size for the resizing element
31881 * @param {Number} size The initial size
31883 setCurrentSize : function(size){
31884 var oldAnimate = this.animate;
31885 this.animate = false;
31886 this.adapter.setElementSize(this, size);
31887 this.animate = oldAnimate;
31891 * Destroy this splitbar.
31892 * @param {Boolean} removeEl True to remove the element
31894 destroy : function(removeEl){
31896 this.shim.remove();
31899 this.proxy.parentNode.removeChild(this.proxy);
31907 * @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.
31909 Roo.bootstrap.SplitBar.createProxy = function(dir){
31910 var proxy = new Roo.Element(document.createElement("div"));
31911 proxy.unselectable();
31912 var cls = 'roo-splitbar-proxy';
31913 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31914 document.body.appendChild(proxy.dom);
31919 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31920 * Default Adapter. It assumes the splitter and resizing element are not positioned
31921 * elements and only gets/sets the width of the element. Generally used for table based layouts.
31923 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31926 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31927 // do nothing for now
31928 init : function(s){
31932 * Called before drag operations to get the current size of the resizing element.
31933 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31935 getElementSize : function(s){
31936 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31937 return s.resizingEl.getWidth();
31939 return s.resizingEl.getHeight();
31944 * Called after drag operations to set the size of the resizing element.
31945 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31946 * @param {Number} newSize The new size to set
31947 * @param {Function} onComplete A function to be invoked when resizing is complete
31949 setElementSize : function(s, newSize, onComplete){
31950 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31952 s.resizingEl.setWidth(newSize);
31954 onComplete(s, newSize);
31957 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31962 s.resizingEl.setHeight(newSize);
31964 onComplete(s, newSize);
31967 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31974 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31975 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31976 * Adapter that moves the splitter element to align with the resized sizing element.
31977 * Used with an absolute positioned SplitBar.
31978 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31979 * document.body, make sure you assign an id to the body element.
31981 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31982 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31983 this.container = Roo.get(container);
31986 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31987 init : function(s){
31988 this.basic.init(s);
31991 getElementSize : function(s){
31992 return this.basic.getElementSize(s);
31995 setElementSize : function(s, newSize, onComplete){
31996 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31999 moveSplitter : function(s){
32000 var yes = Roo.bootstrap.SplitBar;
32001 switch(s.placement){
32003 s.el.setX(s.resizingEl.getRight());
32006 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32009 s.el.setY(s.resizingEl.getBottom());
32012 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32019 * Orientation constant - Create a vertical SplitBar
32023 Roo.bootstrap.SplitBar.VERTICAL = 1;
32026 * Orientation constant - Create a horizontal SplitBar
32030 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32033 * Placement constant - The resizing element is to the left of the splitter element
32037 Roo.bootstrap.SplitBar.LEFT = 1;
32040 * Placement constant - The resizing element is to the right of the splitter element
32044 Roo.bootstrap.SplitBar.RIGHT = 2;
32047 * Placement constant - The resizing element is positioned above the splitter element
32051 Roo.bootstrap.SplitBar.TOP = 3;
32054 * Placement constant - The resizing element is positioned under splitter element
32058 Roo.bootstrap.SplitBar.BOTTOM = 4;
32059 Roo.namespace("Roo.bootstrap.layout");/*
32061 * Ext JS Library 1.1.1
32062 * Copyright(c) 2006-2007, Ext JS, LLC.
32064 * Originally Released Under LGPL - original licence link has changed is not relivant.
32067 * <script type="text/javascript">
32071 * @class Roo.bootstrap.layout.Manager
32072 * @extends Roo.bootstrap.Component
32073 * Base class for layout managers.
32075 Roo.bootstrap.layout.Manager = function(config)
32077 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32083 /** false to disable window resize monitoring @type Boolean */
32084 this.monitorWindowResize = true;
32089 * Fires when a layout is performed.
32090 * @param {Roo.LayoutManager} this
32094 * @event regionresized
32095 * Fires when the user resizes a region.
32096 * @param {Roo.LayoutRegion} region The resized region
32097 * @param {Number} newSize The new size (width for east/west, height for north/south)
32099 "regionresized" : true,
32101 * @event regioncollapsed
32102 * Fires when a region is collapsed.
32103 * @param {Roo.LayoutRegion} region The collapsed region
32105 "regioncollapsed" : true,
32107 * @event regionexpanded
32108 * Fires when a region is expanded.
32109 * @param {Roo.LayoutRegion} region The expanded region
32111 "regionexpanded" : true
32113 this.updating = false;
32116 this.el = Roo.get(config.el);
32122 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32127 monitorWindowResize : true,
32133 onRender : function(ct, position)
32136 this.el = Roo.get(ct);
32139 //this.fireEvent('render',this);
32143 initEvents: function()
32147 // ie scrollbar fix
32148 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32149 document.body.scroll = "no";
32150 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32151 this.el.position('relative');
32153 this.id = this.el.id;
32154 this.el.addClass("roo-layout-container");
32155 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32156 if(this.el.dom != document.body ) {
32157 this.el.on('resize', this.layout,this);
32158 this.el.on('show', this.layout,this);
32164 * Returns true if this layout is currently being updated
32165 * @return {Boolean}
32167 isUpdating : function(){
32168 return this.updating;
32172 * Suspend the LayoutManager from doing auto-layouts while
32173 * making multiple add or remove calls
32175 beginUpdate : function(){
32176 this.updating = true;
32180 * Restore auto-layouts and optionally disable the manager from performing a layout
32181 * @param {Boolean} noLayout true to disable a layout update
32183 endUpdate : function(noLayout){
32184 this.updating = false;
32190 layout: function(){
32194 onRegionResized : function(region, newSize){
32195 this.fireEvent("regionresized", region, newSize);
32199 onRegionCollapsed : function(region){
32200 this.fireEvent("regioncollapsed", region);
32203 onRegionExpanded : function(region){
32204 this.fireEvent("regionexpanded", region);
32208 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32209 * performs box-model adjustments.
32210 * @return {Object} The size as an object {width: (the width), height: (the height)}
32212 getViewSize : function()
32215 if(this.el.dom != document.body){
32216 size = this.el.getSize();
32218 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32220 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32221 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32226 * Returns the Element this layout is bound to.
32227 * @return {Roo.Element}
32229 getEl : function(){
32234 * Returns the specified region.
32235 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32236 * @return {Roo.LayoutRegion}
32238 getRegion : function(target){
32239 return this.regions[target.toLowerCase()];
32242 onWindowResize : function(){
32243 if(this.monitorWindowResize){
32250 * Ext JS Library 1.1.1
32251 * Copyright(c) 2006-2007, Ext JS, LLC.
32253 * Originally Released Under LGPL - original licence link has changed is not relivant.
32256 * <script type="text/javascript">
32259 * @class Roo.bootstrap.layout.Border
32260 * @extends Roo.bootstrap.layout.Manager
32261 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32262 * please see: examples/bootstrap/nested.html<br><br>
32264 <b>The container the layout is rendered into can be either the body element or any other element.
32265 If it is not the body element, the container needs to either be an absolute positioned element,
32266 or you will need to add "position:relative" to the css of the container. You will also need to specify
32267 the container size if it is not the body element.</b>
32270 * Create a new Border
32271 * @param {Object} config Configuration options
32273 Roo.bootstrap.layout.Border = function(config){
32274 config = config || {};
32275 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32279 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32280 if(config[region]){
32281 config[region].region = region;
32282 this.addRegion(config[region]);
32288 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
32290 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32292 * Creates and adds a new region if it doesn't already exist.
32293 * @param {String} target The target region key (north, south, east, west or center).
32294 * @param {Object} config The regions config object
32295 * @return {BorderLayoutRegion} The new region
32297 addRegion : function(config)
32299 if(!this.regions[config.region]){
32300 var r = this.factory(config);
32301 this.bindRegion(r);
32303 return this.regions[config.region];
32307 bindRegion : function(r){
32308 this.regions[r.config.region] = r;
32310 r.on("visibilitychange", this.layout, this);
32311 r.on("paneladded", this.layout, this);
32312 r.on("panelremoved", this.layout, this);
32313 r.on("invalidated", this.layout, this);
32314 r.on("resized", this.onRegionResized, this);
32315 r.on("collapsed", this.onRegionCollapsed, this);
32316 r.on("expanded", this.onRegionExpanded, this);
32320 * Performs a layout update.
32322 layout : function()
32324 if(this.updating) {
32328 // render all the rebions if they have not been done alreayd?
32329 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32330 if(this.regions[region] && !this.regions[region].bodyEl){
32331 this.regions[region].onRender(this.el)
32335 var size = this.getViewSize();
32336 var w = size.width;
32337 var h = size.height;
32342 //var x = 0, y = 0;
32344 var rs = this.regions;
32345 var north = rs["north"];
32346 var south = rs["south"];
32347 var west = rs["west"];
32348 var east = rs["east"];
32349 var center = rs["center"];
32350 //if(this.hideOnLayout){ // not supported anymore
32351 //c.el.setStyle("display", "none");
32353 if(north && north.isVisible()){
32354 var b = north.getBox();
32355 var m = north.getMargins();
32356 b.width = w - (m.left+m.right);
32359 centerY = b.height + b.y + m.bottom;
32360 centerH -= centerY;
32361 north.updateBox(this.safeBox(b));
32363 if(south && south.isVisible()){
32364 var b = south.getBox();
32365 var m = south.getMargins();
32366 b.width = w - (m.left+m.right);
32368 var totalHeight = (b.height + m.top + m.bottom);
32369 b.y = h - totalHeight + m.top;
32370 centerH -= totalHeight;
32371 south.updateBox(this.safeBox(b));
32373 if(west && west.isVisible()){
32374 var b = west.getBox();
32375 var m = west.getMargins();
32376 b.height = centerH - (m.top+m.bottom);
32378 b.y = centerY + m.top;
32379 var totalWidth = (b.width + m.left + m.right);
32380 centerX += totalWidth;
32381 centerW -= totalWidth;
32382 west.updateBox(this.safeBox(b));
32384 if(east && east.isVisible()){
32385 var b = east.getBox();
32386 var m = east.getMargins();
32387 b.height = centerH - (m.top+m.bottom);
32388 var totalWidth = (b.width + m.left + m.right);
32389 b.x = w - totalWidth + m.left;
32390 b.y = centerY + m.top;
32391 centerW -= totalWidth;
32392 east.updateBox(this.safeBox(b));
32395 var m = center.getMargins();
32397 x: centerX + m.left,
32398 y: centerY + m.top,
32399 width: centerW - (m.left+m.right),
32400 height: centerH - (m.top+m.bottom)
32402 //if(this.hideOnLayout){
32403 //center.el.setStyle("display", "block");
32405 center.updateBox(this.safeBox(centerBox));
32408 this.fireEvent("layout", this);
32412 safeBox : function(box){
32413 box.width = Math.max(0, box.width);
32414 box.height = Math.max(0, box.height);
32419 * Adds a ContentPanel (or subclass) to this layout.
32420 * @param {String} target The target region key (north, south, east, west or center).
32421 * @param {Roo.ContentPanel} panel The panel to add
32422 * @return {Roo.ContentPanel} The added panel
32424 add : function(target, panel){
32426 target = target.toLowerCase();
32427 return this.regions[target].add(panel);
32431 * Remove a ContentPanel (or subclass) to this layout.
32432 * @param {String} target The target region key (north, south, east, west or center).
32433 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32434 * @return {Roo.ContentPanel} The removed panel
32436 remove : function(target, panel){
32437 target = target.toLowerCase();
32438 return this.regions[target].remove(panel);
32442 * Searches all regions for a panel with the specified id
32443 * @param {String} panelId
32444 * @return {Roo.ContentPanel} The panel or null if it wasn't found
32446 findPanel : function(panelId){
32447 var rs = this.regions;
32448 for(var target in rs){
32449 if(typeof rs[target] != "function"){
32450 var p = rs[target].getPanel(panelId);
32460 * Searches all regions for a panel with the specified id and activates (shows) it.
32461 * @param {String/ContentPanel} panelId The panels id or the panel itself
32462 * @return {Roo.ContentPanel} The shown panel or null
32464 showPanel : function(panelId) {
32465 var rs = this.regions;
32466 for(var target in rs){
32467 var r = rs[target];
32468 if(typeof r != "function"){
32469 if(r.hasPanel(panelId)){
32470 return r.showPanel(panelId);
32478 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32479 * @param {Roo.state.Provider} provider (optional) An alternate state provider
32482 restoreState : function(provider){
32484 provider = Roo.state.Manager;
32486 var sm = new Roo.LayoutStateManager();
32487 sm.init(this, provider);
32493 * Adds a xtype elements to the layout.
32497 xtype : 'ContentPanel',
32504 xtype : 'NestedLayoutPanel',
32510 items : [ ... list of content panels or nested layout panels.. ]
32514 * @param {Object} cfg Xtype definition of item to add.
32516 addxtype : function(cfg)
32518 // basically accepts a pannel...
32519 // can accept a layout region..!?!?
32520 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32523 // theory? children can only be panels??
32525 //if (!cfg.xtype.match(/Panel$/)) {
32530 if (typeof(cfg.region) == 'undefined') {
32531 Roo.log("Failed to add Panel, region was not set");
32535 var region = cfg.region;
32541 xitems = cfg.items;
32548 case 'Content': // ContentPanel (el, cfg)
32549 case 'Scroll': // ContentPanel (el, cfg)
32551 cfg.autoCreate = true;
32552 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32554 // var el = this.el.createChild();
32555 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32558 this.add(region, ret);
32562 case 'TreePanel': // our new panel!
32563 cfg.el = this.el.createChild();
32564 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32565 this.add(region, ret);
32570 // create a new Layout (which is a Border Layout...
32572 var clayout = cfg.layout;
32573 clayout.el = this.el.createChild();
32574 clayout.items = clayout.items || [];
32578 // replace this exitems with the clayout ones..
32579 xitems = clayout.items;
32581 // force background off if it's in center...
32582 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32583 cfg.background = false;
32585 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
32588 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32589 //console.log('adding nested layout panel ' + cfg.toSource());
32590 this.add(region, ret);
32591 nb = {}; /// find first...
32596 // needs grid and region
32598 //var el = this.getRegion(region).el.createChild();
32600 *var el = this.el.createChild();
32601 // create the grid first...
32602 cfg.grid.container = el;
32603 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
32606 if (region == 'center' && this.active ) {
32607 cfg.background = false;
32610 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32612 this.add(region, ret);
32614 if (cfg.background) {
32615 // render grid on panel activation (if panel background)
32616 ret.on('activate', function(gp) {
32617 if (!gp.grid.rendered) {
32618 // gp.grid.render(el);
32622 // cfg.grid.render(el);
32628 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
32629 // it was the old xcomponent building that caused this before.
32630 // espeically if border is the top element in the tree.
32640 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32642 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32643 this.add(region, ret);
32647 throw "Can not add '" + cfg.xtype + "' to Border";
32653 this.beginUpdate();
32657 Roo.each(xitems, function(i) {
32658 region = nb && i.region ? i.region : false;
32660 var add = ret.addxtype(i);
32663 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32664 if (!i.background) {
32665 abn[region] = nb[region] ;
32672 // make the last non-background panel active..
32673 //if (nb) { Roo.log(abn); }
32676 for(var r in abn) {
32677 region = this.getRegion(r);
32679 // tried using nb[r], but it does not work..
32681 region.showPanel(abn[r]);
32692 factory : function(cfg)
32695 var validRegions = Roo.bootstrap.layout.Border.regions;
32697 var target = cfg.region;
32700 var r = Roo.bootstrap.layout;
32704 return new r.North(cfg);
32706 return new r.South(cfg);
32708 return new r.East(cfg);
32710 return new r.West(cfg);
32712 return new r.Center(cfg);
32714 throw 'Layout region "'+target+'" not supported.';
32721 * Ext JS Library 1.1.1
32722 * Copyright(c) 2006-2007, Ext JS, LLC.
32724 * Originally Released Under LGPL - original licence link has changed is not relivant.
32727 * <script type="text/javascript">
32731 * @class Roo.bootstrap.layout.Basic
32732 * @extends Roo.util.Observable
32733 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32734 * and does not have a titlebar, tabs or any other features. All it does is size and position
32735 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32736 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32737 * @cfg {string} region the region that it inhabits..
32738 * @cfg {bool} skipConfig skip config?
32742 Roo.bootstrap.layout.Basic = function(config){
32744 this.mgr = config.mgr;
32746 this.position = config.region;
32748 var skipConfig = config.skipConfig;
32752 * @scope Roo.BasicLayoutRegion
32756 * @event beforeremove
32757 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32758 * @param {Roo.LayoutRegion} this
32759 * @param {Roo.ContentPanel} panel The panel
32760 * @param {Object} e The cancel event object
32762 "beforeremove" : true,
32764 * @event invalidated
32765 * Fires when the layout for this region is changed.
32766 * @param {Roo.LayoutRegion} this
32768 "invalidated" : true,
32770 * @event visibilitychange
32771 * Fires when this region is shown or hidden
32772 * @param {Roo.LayoutRegion} this
32773 * @param {Boolean} visibility true or false
32775 "visibilitychange" : true,
32777 * @event paneladded
32778 * Fires when a panel is added.
32779 * @param {Roo.LayoutRegion} this
32780 * @param {Roo.ContentPanel} panel The panel
32782 "paneladded" : true,
32784 * @event panelremoved
32785 * Fires when a panel is removed.
32786 * @param {Roo.LayoutRegion} this
32787 * @param {Roo.ContentPanel} panel The panel
32789 "panelremoved" : true,
32791 * @event beforecollapse
32792 * Fires when this region before collapse.
32793 * @param {Roo.LayoutRegion} this
32795 "beforecollapse" : true,
32798 * Fires when this region is collapsed.
32799 * @param {Roo.LayoutRegion} this
32801 "collapsed" : true,
32804 * Fires when this region is expanded.
32805 * @param {Roo.LayoutRegion} this
32810 * Fires when this region is slid into view.
32811 * @param {Roo.LayoutRegion} this
32813 "slideshow" : true,
32816 * Fires when this region slides out of view.
32817 * @param {Roo.LayoutRegion} this
32819 "slidehide" : true,
32821 * @event panelactivated
32822 * Fires when a panel is activated.
32823 * @param {Roo.LayoutRegion} this
32824 * @param {Roo.ContentPanel} panel The activated panel
32826 "panelactivated" : true,
32829 * Fires when the user resizes this region.
32830 * @param {Roo.LayoutRegion} this
32831 * @param {Number} newSize The new size (width for east/west, height for north/south)
32835 /** A collection of panels in this region. @type Roo.util.MixedCollection */
32836 this.panels = new Roo.util.MixedCollection();
32837 this.panels.getKey = this.getPanelId.createDelegate(this);
32839 this.activePanel = null;
32840 // ensure listeners are added...
32842 if (config.listeners || config.events) {
32843 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32844 listeners : config.listeners || {},
32845 events : config.events || {}
32849 if(skipConfig !== true){
32850 this.applyConfig(config);
32854 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32856 getPanelId : function(p){
32860 applyConfig : function(config){
32861 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32862 this.config = config;
32867 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
32868 * the width, for horizontal (north, south) the height.
32869 * @param {Number} newSize The new width or height
32871 resizeTo : function(newSize){
32872 var el = this.el ? this.el :
32873 (this.activePanel ? this.activePanel.getEl() : null);
32875 switch(this.position){
32878 el.setWidth(newSize);
32879 this.fireEvent("resized", this, newSize);
32883 el.setHeight(newSize);
32884 this.fireEvent("resized", this, newSize);
32890 getBox : function(){
32891 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32894 getMargins : function(){
32895 return this.margins;
32898 updateBox : function(box){
32900 var el = this.activePanel.getEl();
32901 el.dom.style.left = box.x + "px";
32902 el.dom.style.top = box.y + "px";
32903 this.activePanel.setSize(box.width, box.height);
32907 * Returns the container element for this region.
32908 * @return {Roo.Element}
32910 getEl : function(){
32911 return this.activePanel;
32915 * Returns true if this region is currently visible.
32916 * @return {Boolean}
32918 isVisible : function(){
32919 return this.activePanel ? true : false;
32922 setActivePanel : function(panel){
32923 panel = this.getPanel(panel);
32924 if(this.activePanel && this.activePanel != panel){
32925 this.activePanel.setActiveState(false);
32926 this.activePanel.getEl().setLeftTop(-10000,-10000);
32928 this.activePanel = panel;
32929 panel.setActiveState(true);
32931 panel.setSize(this.box.width, this.box.height);
32933 this.fireEvent("panelactivated", this, panel);
32934 this.fireEvent("invalidated");
32938 * Show the specified panel.
32939 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32940 * @return {Roo.ContentPanel} The shown panel or null
32942 showPanel : function(panel){
32943 panel = this.getPanel(panel);
32945 this.setActivePanel(panel);
32951 * Get the active panel for this region.
32952 * @return {Roo.ContentPanel} The active panel or null
32954 getActivePanel : function(){
32955 return this.activePanel;
32959 * Add the passed ContentPanel(s)
32960 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32961 * @return {Roo.ContentPanel} The panel added (if only one was added)
32963 add : function(panel){
32964 if(arguments.length > 1){
32965 for(var i = 0, len = arguments.length; i < len; i++) {
32966 this.add(arguments[i]);
32970 if(this.hasPanel(panel)){
32971 this.showPanel(panel);
32974 var el = panel.getEl();
32975 if(el.dom.parentNode != this.mgr.el.dom){
32976 this.mgr.el.dom.appendChild(el.dom);
32978 if(panel.setRegion){
32979 panel.setRegion(this);
32981 this.panels.add(panel);
32982 el.setStyle("position", "absolute");
32983 if(!panel.background){
32984 this.setActivePanel(panel);
32985 if(this.config.initialSize && this.panels.getCount()==1){
32986 this.resizeTo(this.config.initialSize);
32989 this.fireEvent("paneladded", this, panel);
32994 * Returns true if the panel is in this region.
32995 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32996 * @return {Boolean}
32998 hasPanel : function(panel){
32999 if(typeof panel == "object"){ // must be panel obj
33000 panel = panel.getId();
33002 return this.getPanel(panel) ? true : false;
33006 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33007 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33008 * @param {Boolean} preservePanel Overrides the config preservePanel option
33009 * @return {Roo.ContentPanel} The panel that was removed
33011 remove : function(panel, preservePanel){
33012 panel = this.getPanel(panel);
33017 this.fireEvent("beforeremove", this, panel, e);
33018 if(e.cancel === true){
33021 var panelId = panel.getId();
33022 this.panels.removeKey(panelId);
33027 * Returns the panel specified or null if it's not in this region.
33028 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33029 * @return {Roo.ContentPanel}
33031 getPanel : function(id){
33032 if(typeof id == "object"){ // must be panel obj
33035 return this.panels.get(id);
33039 * Returns this regions position (north/south/east/west/center).
33042 getPosition: function(){
33043 return this.position;
33047 * Ext JS Library 1.1.1
33048 * Copyright(c) 2006-2007, Ext JS, LLC.
33050 * Originally Released Under LGPL - original licence link has changed is not relivant.
33053 * <script type="text/javascript">
33057 * @class Roo.bootstrap.layout.Region
33058 * @extends Roo.bootstrap.layout.Basic
33059 * This class represents a region in a layout manager.
33061 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33062 * @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})
33063 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
33064 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
33065 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
33066 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
33067 * @cfg {String} title The title for the region (overrides panel titles)
33068 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
33069 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33070 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
33071 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33072 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
33073 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33074 * the space available, similar to FireFox 1.5 tabs (defaults to false)
33075 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
33076 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
33077 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
33079 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
33080 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
33081 * @cfg {Boolean} disableTabTips True to disable tab tooltips
33082 * @cfg {Number} width For East/West panels
33083 * @cfg {Number} height For North/South panels
33084 * @cfg {Boolean} split To show the splitter
33085 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
33087 * @cfg {string} cls Extra CSS classes to add to region
33089 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
33090 * @cfg {string} region the region that it inhabits..
33093 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
33094 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
33096 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
33097 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
33098 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
33100 Roo.bootstrap.layout.Region = function(config)
33102 this.applyConfig(config);
33104 var mgr = config.mgr;
33105 var pos = config.region;
33106 config.skipConfig = true;
33107 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33110 this.onRender(mgr.el);
33113 this.visible = true;
33114 this.collapsed = false;
33115 this.unrendered_panels = [];
33118 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33120 position: '', // set by wrapper (eg. north/south etc..)
33121 unrendered_panels : null, // unrendered panels.
33122 createBody : function(){
33123 /** This region's body element
33124 * @type Roo.Element */
33125 this.bodyEl = this.el.createChild({
33127 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33131 onRender: function(ctr, pos)
33133 var dh = Roo.DomHelper;
33134 /** This region's container element
33135 * @type Roo.Element */
33136 this.el = dh.append(ctr.dom, {
33138 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33140 /** This region's title element
33141 * @type Roo.Element */
33143 this.titleEl = dh.append(this.el.dom,
33146 unselectable: "on",
33147 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33149 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
33150 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33153 this.titleEl.enableDisplayMode();
33154 /** This region's title text element
33155 * @type HTMLElement */
33156 this.titleTextEl = this.titleEl.dom.firstChild;
33157 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33159 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33160 this.closeBtn.enableDisplayMode();
33161 this.closeBtn.on("click", this.closeClicked, this);
33162 this.closeBtn.hide();
33164 this.createBody(this.config);
33165 if(this.config.hideWhenEmpty){
33167 this.on("paneladded", this.validateVisibility, this);
33168 this.on("panelremoved", this.validateVisibility, this);
33170 if(this.autoScroll){
33171 this.bodyEl.setStyle("overflow", "auto");
33173 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33175 //if(c.titlebar !== false){
33176 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33177 this.titleEl.hide();
33179 this.titleEl.show();
33180 if(this.config.title){
33181 this.titleTextEl.innerHTML = this.config.title;
33185 if(this.config.collapsed){
33186 this.collapse(true);
33188 if(this.config.hidden){
33192 if (this.unrendered_panels && this.unrendered_panels.length) {
33193 for (var i =0;i< this.unrendered_panels.length; i++) {
33194 this.add(this.unrendered_panels[i]);
33196 this.unrendered_panels = null;
33202 applyConfig : function(c)
33205 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33206 var dh = Roo.DomHelper;
33207 if(c.titlebar !== false){
33208 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33209 this.collapseBtn.on("click", this.collapse, this);
33210 this.collapseBtn.enableDisplayMode();
33212 if(c.showPin === true || this.showPin){
33213 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33214 this.stickBtn.enableDisplayMode();
33215 this.stickBtn.on("click", this.expand, this);
33216 this.stickBtn.hide();
33221 /** This region's collapsed element
33222 * @type Roo.Element */
33225 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33226 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33229 if(c.floatable !== false){
33230 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33231 this.collapsedEl.on("click", this.collapseClick, this);
33234 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33235 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33236 id: "message", unselectable: "on", style:{"float":"left"}});
33237 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33239 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33240 this.expandBtn.on("click", this.expand, this);
33244 if(this.collapseBtn){
33245 this.collapseBtn.setVisible(c.collapsible == true);
33248 this.cmargins = c.cmargins || this.cmargins ||
33249 (this.position == "west" || this.position == "east" ?
33250 {top: 0, left: 2, right:2, bottom: 0} :
33251 {top: 2, left: 0, right:0, bottom: 2});
33253 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33256 this.bottomTabs = c.tabPosition != "top";
33258 this.autoScroll = c.autoScroll || false;
33263 this.duration = c.duration || .30;
33264 this.slideDuration = c.slideDuration || .45;
33269 * Returns true if this region is currently visible.
33270 * @return {Boolean}
33272 isVisible : function(){
33273 return this.visible;
33277 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33278 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
33280 //setCollapsedTitle : function(title){
33281 // title = title || " ";
33282 // if(this.collapsedTitleTextEl){
33283 // this.collapsedTitleTextEl.innerHTML = title;
33287 getBox : function(){
33289 // if(!this.collapsed){
33290 b = this.el.getBox(false, true);
33292 // b = this.collapsedEl.getBox(false, true);
33297 getMargins : function(){
33298 return this.margins;
33299 //return this.collapsed ? this.cmargins : this.margins;
33302 highlight : function(){
33303 this.el.addClass("x-layout-panel-dragover");
33306 unhighlight : function(){
33307 this.el.removeClass("x-layout-panel-dragover");
33310 updateBox : function(box)
33312 if (!this.bodyEl) {
33313 return; // not rendered yet..
33317 if(!this.collapsed){
33318 this.el.dom.style.left = box.x + "px";
33319 this.el.dom.style.top = box.y + "px";
33320 this.updateBody(box.width, box.height);
33322 this.collapsedEl.dom.style.left = box.x + "px";
33323 this.collapsedEl.dom.style.top = box.y + "px";
33324 this.collapsedEl.setSize(box.width, box.height);
33327 this.tabs.autoSizeTabs();
33331 updateBody : function(w, h)
33334 this.el.setWidth(w);
33335 w -= this.el.getBorderWidth("rl");
33336 if(this.config.adjustments){
33337 w += this.config.adjustments[0];
33340 if(h !== null && h > 0){
33341 this.el.setHeight(h);
33342 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33343 h -= this.el.getBorderWidth("tb");
33344 if(this.config.adjustments){
33345 h += this.config.adjustments[1];
33347 this.bodyEl.setHeight(h);
33349 h = this.tabs.syncHeight(h);
33352 if(this.panelSize){
33353 w = w !== null ? w : this.panelSize.width;
33354 h = h !== null ? h : this.panelSize.height;
33356 if(this.activePanel){
33357 var el = this.activePanel.getEl();
33358 w = w !== null ? w : el.getWidth();
33359 h = h !== null ? h : el.getHeight();
33360 this.panelSize = {width: w, height: h};
33361 this.activePanel.setSize(w, h);
33363 if(Roo.isIE && this.tabs){
33364 this.tabs.el.repaint();
33369 * Returns the container element for this region.
33370 * @return {Roo.Element}
33372 getEl : function(){
33377 * Hides this region.
33380 //if(!this.collapsed){
33381 this.el.dom.style.left = "-2000px";
33384 // this.collapsedEl.dom.style.left = "-2000px";
33385 // this.collapsedEl.hide();
33387 this.visible = false;
33388 this.fireEvent("visibilitychange", this, false);
33392 * Shows this region if it was previously hidden.
33395 //if(!this.collapsed){
33398 // this.collapsedEl.show();
33400 this.visible = true;
33401 this.fireEvent("visibilitychange", this, true);
33404 closeClicked : function(){
33405 if(this.activePanel){
33406 this.remove(this.activePanel);
33410 collapseClick : function(e){
33412 e.stopPropagation();
33415 e.stopPropagation();
33421 * Collapses this region.
33422 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33425 collapse : function(skipAnim, skipCheck = false){
33426 if(this.collapsed) {
33430 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33432 this.collapsed = true;
33434 this.split.el.hide();
33436 if(this.config.animate && skipAnim !== true){
33437 this.fireEvent("invalidated", this);
33438 this.animateCollapse();
33440 this.el.setLocation(-20000,-20000);
33442 this.collapsedEl.show();
33443 this.fireEvent("collapsed", this);
33444 this.fireEvent("invalidated", this);
33450 animateCollapse : function(){
33455 * Expands this region if it was previously collapsed.
33456 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33457 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33460 expand : function(e, skipAnim){
33462 e.stopPropagation();
33464 if(!this.collapsed || this.el.hasActiveFx()) {
33468 this.afterSlideIn();
33471 this.collapsed = false;
33472 if(this.config.animate && skipAnim !== true){
33473 this.animateExpand();
33477 this.split.el.show();
33479 this.collapsedEl.setLocation(-2000,-2000);
33480 this.collapsedEl.hide();
33481 this.fireEvent("invalidated", this);
33482 this.fireEvent("expanded", this);
33486 animateExpand : function(){
33490 initTabs : function()
33492 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33494 var ts = new Roo.bootstrap.panel.Tabs({
33495 el: this.bodyEl.dom,
33496 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33497 disableTooltips: this.config.disableTabTips,
33498 toolbar : this.config.toolbar
33501 if(this.config.hideTabs){
33502 ts.stripWrap.setDisplayed(false);
33505 ts.resizeTabs = this.config.resizeTabs === true;
33506 ts.minTabWidth = this.config.minTabWidth || 40;
33507 ts.maxTabWidth = this.config.maxTabWidth || 250;
33508 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33509 ts.monitorResize = false;
33510 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33511 ts.bodyEl.addClass('roo-layout-tabs-body');
33512 this.panels.each(this.initPanelAsTab, this);
33515 initPanelAsTab : function(panel){
33516 var ti = this.tabs.addTab(
33520 this.config.closeOnTab && panel.isClosable(),
33523 if(panel.tabTip !== undefined){
33524 ti.setTooltip(panel.tabTip);
33526 ti.on("activate", function(){
33527 this.setActivePanel(panel);
33530 if(this.config.closeOnTab){
33531 ti.on("beforeclose", function(t, e){
33533 this.remove(panel);
33537 panel.tabItem = ti;
33542 updatePanelTitle : function(panel, title)
33544 if(this.activePanel == panel){
33545 this.updateTitle(title);
33548 var ti = this.tabs.getTab(panel.getEl().id);
33550 if(panel.tabTip !== undefined){
33551 ti.setTooltip(panel.tabTip);
33556 updateTitle : function(title){
33557 if(this.titleTextEl && !this.config.title){
33558 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
33562 setActivePanel : function(panel)
33564 panel = this.getPanel(panel);
33565 if(this.activePanel && this.activePanel != panel){
33566 this.activePanel.setActiveState(false);
33568 this.activePanel = panel;
33569 panel.setActiveState(true);
33570 if(this.panelSize){
33571 panel.setSize(this.panelSize.width, this.panelSize.height);
33574 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33576 this.updateTitle(panel.getTitle());
33578 this.fireEvent("invalidated", this);
33580 this.fireEvent("panelactivated", this, panel);
33584 * Shows the specified panel.
33585 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33586 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33588 showPanel : function(panel)
33590 panel = this.getPanel(panel);
33593 var tab = this.tabs.getTab(panel.getEl().id);
33594 if(tab.isHidden()){
33595 this.tabs.unhideTab(tab.id);
33599 this.setActivePanel(panel);
33606 * Get the active panel for this region.
33607 * @return {Roo.ContentPanel} The active panel or null
33609 getActivePanel : function(){
33610 return this.activePanel;
33613 validateVisibility : function(){
33614 if(this.panels.getCount() < 1){
33615 this.updateTitle(" ");
33616 this.closeBtn.hide();
33619 if(!this.isVisible()){
33626 * Adds the passed ContentPanel(s) to this region.
33627 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33628 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33630 add : function(panel)
33632 if(arguments.length > 1){
33633 for(var i = 0, len = arguments.length; i < len; i++) {
33634 this.add(arguments[i]);
33639 // if we have not been rendered yet, then we can not really do much of this..
33640 if (!this.bodyEl) {
33641 this.unrendered_panels.push(panel);
33648 if(this.hasPanel(panel)){
33649 this.showPanel(panel);
33652 panel.setRegion(this);
33653 this.panels.add(panel);
33654 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33655 // sinle panel - no tab...?? would it not be better to render it with the tabs,
33656 // and hide them... ???
33657 this.bodyEl.dom.appendChild(panel.getEl().dom);
33658 if(panel.background !== true){
33659 this.setActivePanel(panel);
33661 this.fireEvent("paneladded", this, panel);
33668 this.initPanelAsTab(panel);
33672 if(panel.background !== true){
33673 this.tabs.activate(panel.getEl().id);
33675 this.fireEvent("paneladded", this, panel);
33680 * Hides the tab for the specified panel.
33681 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33683 hidePanel : function(panel){
33684 if(this.tabs && (panel = this.getPanel(panel))){
33685 this.tabs.hideTab(panel.getEl().id);
33690 * Unhides the tab for a previously hidden panel.
33691 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33693 unhidePanel : function(panel){
33694 if(this.tabs && (panel = this.getPanel(panel))){
33695 this.tabs.unhideTab(panel.getEl().id);
33699 clearPanels : function(){
33700 while(this.panels.getCount() > 0){
33701 this.remove(this.panels.first());
33706 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33707 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33708 * @param {Boolean} preservePanel Overrides the config preservePanel option
33709 * @return {Roo.ContentPanel} The panel that was removed
33711 remove : function(panel, preservePanel)
33713 panel = this.getPanel(panel);
33718 this.fireEvent("beforeremove", this, panel, e);
33719 if(e.cancel === true){
33722 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33723 var panelId = panel.getId();
33724 this.panels.removeKey(panelId);
33726 document.body.appendChild(panel.getEl().dom);
33729 this.tabs.removeTab(panel.getEl().id);
33730 }else if (!preservePanel){
33731 this.bodyEl.dom.removeChild(panel.getEl().dom);
33733 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33734 var p = this.panels.first();
33735 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33736 tempEl.appendChild(p.getEl().dom);
33737 this.bodyEl.update("");
33738 this.bodyEl.dom.appendChild(p.getEl().dom);
33740 this.updateTitle(p.getTitle());
33742 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33743 this.setActivePanel(p);
33745 panel.setRegion(null);
33746 if(this.activePanel == panel){
33747 this.activePanel = null;
33749 if(this.config.autoDestroy !== false && preservePanel !== true){
33750 try{panel.destroy();}catch(e){}
33752 this.fireEvent("panelremoved", this, panel);
33757 * Returns the TabPanel component used by this region
33758 * @return {Roo.TabPanel}
33760 getTabs : function(){
33764 createTool : function(parentEl, className){
33765 var btn = Roo.DomHelper.append(parentEl, {
33767 cls: "x-layout-tools-button",
33770 cls: "roo-layout-tools-button-inner " + className,
33774 btn.addClassOnOver("roo-layout-tools-button-over");
33779 * Ext JS Library 1.1.1
33780 * Copyright(c) 2006-2007, Ext JS, LLC.
33782 * Originally Released Under LGPL - original licence link has changed is not relivant.
33785 * <script type="text/javascript">
33791 * @class Roo.SplitLayoutRegion
33792 * @extends Roo.LayoutRegion
33793 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33795 Roo.bootstrap.layout.Split = function(config){
33796 this.cursor = config.cursor;
33797 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33800 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33802 splitTip : "Drag to resize.",
33803 collapsibleSplitTip : "Drag to resize. Double click to hide.",
33804 useSplitTips : false,
33806 applyConfig : function(config){
33807 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33810 onRender : function(ctr,pos) {
33812 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33813 if(!this.config.split){
33818 var splitEl = Roo.DomHelper.append(ctr.dom, {
33820 id: this.el.id + "-split",
33821 cls: "roo-layout-split roo-layout-split-"+this.position,
33824 /** The SplitBar for this region
33825 * @type Roo.SplitBar */
33826 // does not exist yet...
33827 Roo.log([this.position, this.orientation]);
33829 this.split = new Roo.bootstrap.SplitBar({
33830 dragElement : splitEl,
33831 resizingElement: this.el,
33832 orientation : this.orientation
33835 this.split.on("moved", this.onSplitMove, this);
33836 this.split.useShim = this.config.useShim === true;
33837 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33838 if(this.useSplitTips){
33839 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33841 //if(config.collapsible){
33842 // this.split.el.on("dblclick", this.collapse, this);
33845 if(typeof this.config.minSize != "undefined"){
33846 this.split.minSize = this.config.minSize;
33848 if(typeof this.config.maxSize != "undefined"){
33849 this.split.maxSize = this.config.maxSize;
33851 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33852 this.hideSplitter();
33857 getHMaxSize : function(){
33858 var cmax = this.config.maxSize || 10000;
33859 var center = this.mgr.getRegion("center");
33860 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33863 getVMaxSize : function(){
33864 var cmax = this.config.maxSize || 10000;
33865 var center = this.mgr.getRegion("center");
33866 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33869 onSplitMove : function(split, newSize){
33870 this.fireEvent("resized", this, newSize);
33874 * Returns the {@link Roo.SplitBar} for this region.
33875 * @return {Roo.SplitBar}
33877 getSplitBar : function(){
33882 this.hideSplitter();
33883 Roo.bootstrap.layout.Split.superclass.hide.call(this);
33886 hideSplitter : function(){
33888 this.split.el.setLocation(-2000,-2000);
33889 this.split.el.hide();
33895 this.split.el.show();
33897 Roo.bootstrap.layout.Split.superclass.show.call(this);
33900 beforeSlide: function(){
33901 if(Roo.isGecko){// firefox overflow auto bug workaround
33902 this.bodyEl.clip();
33904 this.tabs.bodyEl.clip();
33906 if(this.activePanel){
33907 this.activePanel.getEl().clip();
33909 if(this.activePanel.beforeSlide){
33910 this.activePanel.beforeSlide();
33916 afterSlide : function(){
33917 if(Roo.isGecko){// firefox overflow auto bug workaround
33918 this.bodyEl.unclip();
33920 this.tabs.bodyEl.unclip();
33922 if(this.activePanel){
33923 this.activePanel.getEl().unclip();
33924 if(this.activePanel.afterSlide){
33925 this.activePanel.afterSlide();
33931 initAutoHide : function(){
33932 if(this.autoHide !== false){
33933 if(!this.autoHideHd){
33934 var st = new Roo.util.DelayedTask(this.slideIn, this);
33935 this.autoHideHd = {
33936 "mouseout": function(e){
33937 if(!e.within(this.el, true)){
33941 "mouseover" : function(e){
33947 this.el.on(this.autoHideHd);
33951 clearAutoHide : function(){
33952 if(this.autoHide !== false){
33953 this.el.un("mouseout", this.autoHideHd.mouseout);
33954 this.el.un("mouseover", this.autoHideHd.mouseover);
33958 clearMonitor : function(){
33959 Roo.get(document).un("click", this.slideInIf, this);
33962 // these names are backwards but not changed for compat
33963 slideOut : function(){
33964 if(this.isSlid || this.el.hasActiveFx()){
33967 this.isSlid = true;
33968 if(this.collapseBtn){
33969 this.collapseBtn.hide();
33971 this.closeBtnState = this.closeBtn.getStyle('display');
33972 this.closeBtn.hide();
33974 this.stickBtn.show();
33977 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33978 this.beforeSlide();
33979 this.el.setStyle("z-index", 10001);
33980 this.el.slideIn(this.getSlideAnchor(), {
33981 callback: function(){
33983 this.initAutoHide();
33984 Roo.get(document).on("click", this.slideInIf, this);
33985 this.fireEvent("slideshow", this);
33992 afterSlideIn : function(){
33993 this.clearAutoHide();
33994 this.isSlid = false;
33995 this.clearMonitor();
33996 this.el.setStyle("z-index", "");
33997 if(this.collapseBtn){
33998 this.collapseBtn.show();
34000 this.closeBtn.setStyle('display', this.closeBtnState);
34002 this.stickBtn.hide();
34004 this.fireEvent("slidehide", this);
34007 slideIn : function(cb){
34008 if(!this.isSlid || this.el.hasActiveFx()){
34012 this.isSlid = false;
34013 this.beforeSlide();
34014 this.el.slideOut(this.getSlideAnchor(), {
34015 callback: function(){
34016 this.el.setLeftTop(-10000, -10000);
34018 this.afterSlideIn();
34026 slideInIf : function(e){
34027 if(!e.within(this.el)){
34032 animateCollapse : function(){
34033 this.beforeSlide();
34034 this.el.setStyle("z-index", 20000);
34035 var anchor = this.getSlideAnchor();
34036 this.el.slideOut(anchor, {
34037 callback : function(){
34038 this.el.setStyle("z-index", "");
34039 this.collapsedEl.slideIn(anchor, {duration:.3});
34041 this.el.setLocation(-10000,-10000);
34043 this.fireEvent("collapsed", this);
34050 animateExpand : function(){
34051 this.beforeSlide();
34052 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34053 this.el.setStyle("z-index", 20000);
34054 this.collapsedEl.hide({
34057 this.el.slideIn(this.getSlideAnchor(), {
34058 callback : function(){
34059 this.el.setStyle("z-index", "");
34062 this.split.el.show();
34064 this.fireEvent("invalidated", this);
34065 this.fireEvent("expanded", this);
34093 getAnchor : function(){
34094 return this.anchors[this.position];
34097 getCollapseAnchor : function(){
34098 return this.canchors[this.position];
34101 getSlideAnchor : function(){
34102 return this.sanchors[this.position];
34105 getAlignAdj : function(){
34106 var cm = this.cmargins;
34107 switch(this.position){
34123 getExpandAdj : function(){
34124 var c = this.collapsedEl, cm = this.cmargins;
34125 switch(this.position){
34127 return [-(cm.right+c.getWidth()+cm.left), 0];
34130 return [cm.right+c.getWidth()+cm.left, 0];
34133 return [0, -(cm.top+cm.bottom+c.getHeight())];
34136 return [0, cm.top+cm.bottom+c.getHeight()];
34142 * Ext JS Library 1.1.1
34143 * Copyright(c) 2006-2007, Ext JS, LLC.
34145 * Originally Released Under LGPL - original licence link has changed is not relivant.
34148 * <script type="text/javascript">
34151 * These classes are private internal classes
34153 Roo.bootstrap.layout.Center = function(config){
34154 config.region = "center";
34155 Roo.bootstrap.layout.Region.call(this, config);
34156 this.visible = true;
34157 this.minWidth = config.minWidth || 20;
34158 this.minHeight = config.minHeight || 20;
34161 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34163 // center panel can't be hidden
34167 // center panel can't be hidden
34170 getMinWidth: function(){
34171 return this.minWidth;
34174 getMinHeight: function(){
34175 return this.minHeight;
34188 Roo.bootstrap.layout.North = function(config)
34190 config.region = 'north';
34191 config.cursor = 'n-resize';
34193 Roo.bootstrap.layout.Split.call(this, config);
34197 this.split.placement = Roo.bootstrap.SplitBar.TOP;
34198 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34199 this.split.el.addClass("roo-layout-split-v");
34201 var size = config.initialSize || config.height;
34202 if(typeof size != "undefined"){
34203 this.el.setHeight(size);
34206 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34208 orientation: Roo.bootstrap.SplitBar.VERTICAL,
34212 getBox : function(){
34213 if(this.collapsed){
34214 return this.collapsedEl.getBox();
34216 var box = this.el.getBox();
34218 box.height += this.split.el.getHeight();
34223 updateBox : function(box){
34224 if(this.split && !this.collapsed){
34225 box.height -= this.split.el.getHeight();
34226 this.split.el.setLeft(box.x);
34227 this.split.el.setTop(box.y+box.height);
34228 this.split.el.setWidth(box.width);
34230 if(this.collapsed){
34231 this.updateBody(box.width, null);
34233 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34241 Roo.bootstrap.layout.South = function(config){
34242 config.region = 'south';
34243 config.cursor = 's-resize';
34244 Roo.bootstrap.layout.Split.call(this, config);
34246 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34247 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34248 this.split.el.addClass("roo-layout-split-v");
34250 var size = config.initialSize || config.height;
34251 if(typeof size != "undefined"){
34252 this.el.setHeight(size);
34256 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34257 orientation: Roo.bootstrap.SplitBar.VERTICAL,
34258 getBox : function(){
34259 if(this.collapsed){
34260 return this.collapsedEl.getBox();
34262 var box = this.el.getBox();
34264 var sh = this.split.el.getHeight();
34271 updateBox : function(box){
34272 if(this.split && !this.collapsed){
34273 var sh = this.split.el.getHeight();
34276 this.split.el.setLeft(box.x);
34277 this.split.el.setTop(box.y-sh);
34278 this.split.el.setWidth(box.width);
34280 if(this.collapsed){
34281 this.updateBody(box.width, null);
34283 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34287 Roo.bootstrap.layout.East = function(config){
34288 config.region = "east";
34289 config.cursor = "e-resize";
34290 Roo.bootstrap.layout.Split.call(this, config);
34292 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34293 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34294 this.split.el.addClass("roo-layout-split-h");
34296 var size = config.initialSize || config.width;
34297 if(typeof size != "undefined"){
34298 this.el.setWidth(size);
34301 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34302 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34303 getBox : function(){
34304 if(this.collapsed){
34305 return this.collapsedEl.getBox();
34307 var box = this.el.getBox();
34309 var sw = this.split.el.getWidth();
34316 updateBox : function(box){
34317 if(this.split && !this.collapsed){
34318 var sw = this.split.el.getWidth();
34320 this.split.el.setLeft(box.x);
34321 this.split.el.setTop(box.y);
34322 this.split.el.setHeight(box.height);
34325 if(this.collapsed){
34326 this.updateBody(null, box.height);
34328 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34332 Roo.bootstrap.layout.West = function(config){
34333 config.region = "west";
34334 config.cursor = "w-resize";
34336 Roo.bootstrap.layout.Split.call(this, config);
34338 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34339 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34340 this.split.el.addClass("roo-layout-split-h");
34344 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34345 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34347 onRender: function(ctr, pos)
34349 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34350 var size = this.config.initialSize || this.config.width;
34351 if(typeof size != "undefined"){
34352 this.el.setWidth(size);
34356 getBox : function(){
34357 if(this.collapsed){
34358 return this.collapsedEl.getBox();
34360 var box = this.el.getBox();
34362 box.width += this.split.el.getWidth();
34367 updateBox : function(box){
34368 if(this.split && !this.collapsed){
34369 var sw = this.split.el.getWidth();
34371 this.split.el.setLeft(box.x+box.width);
34372 this.split.el.setTop(box.y);
34373 this.split.el.setHeight(box.height);
34375 if(this.collapsed){
34376 this.updateBody(null, box.height);
34378 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34381 Roo.namespace("Roo.bootstrap.panel");/*
34383 * Ext JS Library 1.1.1
34384 * Copyright(c) 2006-2007, Ext JS, LLC.
34386 * Originally Released Under LGPL - original licence link has changed is not relivant.
34389 * <script type="text/javascript">
34392 * @class Roo.ContentPanel
34393 * @extends Roo.util.Observable
34394 * A basic ContentPanel element.
34395 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
34396 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
34397 * @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
34398 * @cfg {Boolean} closable True if the panel can be closed/removed
34399 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
34400 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34401 * @cfg {Toolbar} toolbar A toolbar for this panel
34402 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
34403 * @cfg {String} title The title for this panel
34404 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34405 * @cfg {String} url Calls {@link #setUrl} with this value
34406 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34407 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
34408 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
34409 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
34410 * @cfg {Boolean} badges render the badges
34413 * Create a new ContentPanel.
34414 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34415 * @param {String/Object} config A string to set only the title or a config object
34416 * @param {String} content (optional) Set the HTML content for this panel
34417 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34419 Roo.bootstrap.panel.Content = function( config){
34421 this.tpl = config.tpl || false;
34423 var el = config.el;
34424 var content = config.content;
34426 if(config.autoCreate){ // xtype is available if this is called from factory
34429 this.el = Roo.get(el);
34430 if(!this.el && config && config.autoCreate){
34431 if(typeof config.autoCreate == "object"){
34432 if(!config.autoCreate.id){
34433 config.autoCreate.id = config.id||el;
34435 this.el = Roo.DomHelper.append(document.body,
34436 config.autoCreate, true);
34438 var elcfg = { tag: "div",
34439 cls: "roo-layout-inactive-content",
34443 elcfg.html = config.html;
34447 this.el = Roo.DomHelper.append(document.body, elcfg , true);
34450 this.closable = false;
34451 this.loaded = false;
34452 this.active = false;
34455 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
34457 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
34459 this.wrapEl = this.el; //this.el.wrap();
34461 if (config.toolbar.items) {
34462 ti = config.toolbar.items ;
34463 delete config.toolbar.items ;
34467 this.toolbar.render(this.wrapEl, 'before');
34468 for(var i =0;i < ti.length;i++) {
34469 // Roo.log(['add child', items[i]]);
34470 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34472 this.toolbar.items = nitems;
34473 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34474 delete config.toolbar;
34478 // xtype created footer. - not sure if will work as we normally have to render first..
34479 if (this.footer && !this.footer.el && this.footer.xtype) {
34480 if (!this.wrapEl) {
34481 this.wrapEl = this.el.wrap();
34484 this.footer.container = this.wrapEl.createChild();
34486 this.footer = Roo.factory(this.footer, Roo);
34491 if(typeof config == "string"){
34492 this.title = config;
34494 Roo.apply(this, config);
34498 this.resizeEl = Roo.get(this.resizeEl, true);
34500 this.resizeEl = this.el;
34502 // handle view.xtype
34510 * Fires when this panel is activated.
34511 * @param {Roo.ContentPanel} this
34515 * @event deactivate
34516 * Fires when this panel is activated.
34517 * @param {Roo.ContentPanel} this
34519 "deactivate" : true,
34523 * Fires when this panel is resized if fitToFrame is true.
34524 * @param {Roo.ContentPanel} this
34525 * @param {Number} width The width after any component adjustments
34526 * @param {Number} height The height after any component adjustments
34532 * Fires when this tab is created
34533 * @param {Roo.ContentPanel} this
34544 if(this.autoScroll){
34545 this.resizeEl.setStyle("overflow", "auto");
34547 // fix randome scrolling
34548 //this.el.on('scroll', function() {
34549 // Roo.log('fix random scolling');
34550 // this.scrollTo('top',0);
34553 content = content || this.content;
34555 this.setContent(content);
34557 if(config && config.url){
34558 this.setUrl(this.url, this.params, this.loadOnce);
34563 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34565 if (this.view && typeof(this.view.xtype) != 'undefined') {
34566 this.view.el = this.el.appendChild(document.createElement("div"));
34567 this.view = Roo.factory(this.view);
34568 this.view.render && this.view.render(false, '');
34572 this.fireEvent('render', this);
34575 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34579 setRegion : function(region){
34580 this.region = region;
34581 this.setActiveClass(region && !this.background);
34585 setActiveClass: function(state)
34588 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
34589 this.el.setStyle('position','relative');
34591 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
34592 this.el.setStyle('position', 'absolute');
34597 * Returns the toolbar for this Panel if one was configured.
34598 * @return {Roo.Toolbar}
34600 getToolbar : function(){
34601 return this.toolbar;
34604 setActiveState : function(active)
34606 this.active = active;
34607 this.setActiveClass(active);
34609 this.fireEvent("deactivate", this);
34611 this.fireEvent("activate", this);
34615 * Updates this panel's element
34616 * @param {String} content The new content
34617 * @param {Boolean} loadScripts (optional) true to look for and process scripts
34619 setContent : function(content, loadScripts){
34620 this.el.update(content, loadScripts);
34623 ignoreResize : function(w, h){
34624 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34627 this.lastSize = {width: w, height: h};
34632 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34633 * @return {Roo.UpdateManager} The UpdateManager
34635 getUpdateManager : function(){
34636 return this.el.getUpdateManager();
34639 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34640 * @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:
34643 url: "your-url.php",
34644 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34645 callback: yourFunction,
34646 scope: yourObject, //(optional scope)
34649 text: "Loading...",
34654 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34655 * 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.
34656 * @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}
34657 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34658 * @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.
34659 * @return {Roo.ContentPanel} this
34662 var um = this.el.getUpdateManager();
34663 um.update.apply(um, arguments);
34669 * 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.
34670 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34671 * @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)
34672 * @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)
34673 * @return {Roo.UpdateManager} The UpdateManager
34675 setUrl : function(url, params, loadOnce){
34676 if(this.refreshDelegate){
34677 this.removeListener("activate", this.refreshDelegate);
34679 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34680 this.on("activate", this.refreshDelegate);
34681 return this.el.getUpdateManager();
34684 _handleRefresh : function(url, params, loadOnce){
34685 if(!loadOnce || !this.loaded){
34686 var updater = this.el.getUpdateManager();
34687 updater.update(url, params, this._setLoaded.createDelegate(this));
34691 _setLoaded : function(){
34692 this.loaded = true;
34696 * Returns this panel's id
34699 getId : function(){
34704 * Returns this panel's element - used by regiosn to add.
34705 * @return {Roo.Element}
34707 getEl : function(){
34708 return this.wrapEl || this.el;
34713 adjustForComponents : function(width, height)
34715 //Roo.log('adjustForComponents ');
34716 if(this.resizeEl != this.el){
34717 width -= this.el.getFrameWidth('lr');
34718 height -= this.el.getFrameWidth('tb');
34721 var te = this.toolbar.getEl();
34722 height -= te.getHeight();
34723 te.setWidth(width);
34726 var te = this.footer.getEl();
34727 Roo.log("footer:" + te.getHeight());
34729 height -= te.getHeight();
34730 te.setWidth(width);
34734 if(this.adjustments){
34735 width += this.adjustments[0];
34736 height += this.adjustments[1];
34738 return {"width": width, "height": height};
34741 setSize : function(width, height){
34742 if(this.fitToFrame && !this.ignoreResize(width, height)){
34743 if(this.fitContainer && this.resizeEl != this.el){
34744 this.el.setSize(width, height);
34746 var size = this.adjustForComponents(width, height);
34747 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34748 this.fireEvent('resize', this, size.width, size.height);
34753 * Returns this panel's title
34756 getTitle : function(){
34761 * Set this panel's title
34762 * @param {String} title
34764 setTitle : function(title){
34765 this.title = title;
34767 this.region.updatePanelTitle(this, title);
34772 * Returns true is this panel was configured to be closable
34773 * @return {Boolean}
34775 isClosable : function(){
34776 return this.closable;
34779 beforeSlide : function(){
34781 this.resizeEl.clip();
34784 afterSlide : function(){
34786 this.resizeEl.unclip();
34790 * Force a content refresh from the URL specified in the {@link #setUrl} method.
34791 * Will fail silently if the {@link #setUrl} method has not been called.
34792 * This does not activate the panel, just updates its content.
34794 refresh : function(){
34795 if(this.refreshDelegate){
34796 this.loaded = false;
34797 this.refreshDelegate();
34802 * Destroys this panel
34804 destroy : function(){
34805 this.el.removeAllListeners();
34806 var tempEl = document.createElement("span");
34807 tempEl.appendChild(this.el.dom);
34808 tempEl.innerHTML = "";
34814 * form - if the content panel contains a form - this is a reference to it.
34815 * @type {Roo.form.Form}
34819 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34820 * This contains a reference to it.
34826 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34836 * @param {Object} cfg Xtype definition of item to add.
34840 getChildContainer: function () {
34841 return this.getEl();
34846 var ret = new Roo.factory(cfg);
34851 if (cfg.xtype.match(/^Form$/)) {
34854 //if (this.footer) {
34855 // el = this.footer.container.insertSibling(false, 'before');
34857 el = this.el.createChild();
34860 this.form = new Roo.form.Form(cfg);
34863 if ( this.form.allItems.length) {
34864 this.form.render(el.dom);
34868 // should only have one of theses..
34869 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34870 // views.. should not be just added - used named prop 'view''
34872 cfg.el = this.el.appendChild(document.createElement("div"));
34875 var ret = new Roo.factory(cfg);
34877 ret.render && ret.render(false, ''); // render blank..
34887 * @class Roo.bootstrap.panel.Grid
34888 * @extends Roo.bootstrap.panel.Content
34890 * Create a new GridPanel.
34891 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34892 * @param {Object} config A the config object
34898 Roo.bootstrap.panel.Grid = function(config)
34902 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34903 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34905 config.el = this.wrapper;
34906 //this.el = this.wrapper;
34908 if (config.container) {
34909 // ctor'ed from a Border/panel.grid
34912 this.wrapper.setStyle("overflow", "hidden");
34913 this.wrapper.addClass('roo-grid-container');
34918 if(config.toolbar){
34919 var tool_el = this.wrapper.createChild();
34920 this.toolbar = Roo.factory(config.toolbar);
34922 if (config.toolbar.items) {
34923 ti = config.toolbar.items ;
34924 delete config.toolbar.items ;
34928 this.toolbar.render(tool_el);
34929 for(var i =0;i < ti.length;i++) {
34930 // Roo.log(['add child', items[i]]);
34931 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34933 this.toolbar.items = nitems;
34935 delete config.toolbar;
34938 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34939 config.grid.scrollBody = true;;
34940 config.grid.monitorWindowResize = false; // turn off autosizing
34941 config.grid.autoHeight = false;
34942 config.grid.autoWidth = false;
34944 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34946 if (config.background) {
34947 // render grid on panel activation (if panel background)
34948 this.on('activate', function(gp) {
34949 if (!gp.grid.rendered) {
34950 gp.grid.render(this.wrapper);
34951 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34956 this.grid.render(this.wrapper);
34957 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34960 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34961 // ??? needed ??? config.el = this.wrapper;
34966 // xtype created footer. - not sure if will work as we normally have to render first..
34967 if (this.footer && !this.footer.el && this.footer.xtype) {
34969 var ctr = this.grid.getView().getFooterPanel(true);
34970 this.footer.dataSource = this.grid.dataSource;
34971 this.footer = Roo.factory(this.footer, Roo);
34972 this.footer.render(ctr);
34982 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34983 getId : function(){
34984 return this.grid.id;
34988 * Returns the grid for this panel
34989 * @return {Roo.bootstrap.Table}
34991 getGrid : function(){
34995 setSize : function(width, height){
34996 if(!this.ignoreResize(width, height)){
34997 var grid = this.grid;
34998 var size = this.adjustForComponents(width, height);
34999 var gridel = grid.getGridEl();
35000 gridel.setSize(size.width, size.height);
35002 var thd = grid.getGridEl().select('thead',true).first();
35003 var tbd = grid.getGridEl().select('tbody', true).first();
35005 tbd.setSize(width, height - thd.getHeight());
35014 beforeSlide : function(){
35015 this.grid.getView().scroller.clip();
35018 afterSlide : function(){
35019 this.grid.getView().scroller.unclip();
35022 destroy : function(){
35023 this.grid.destroy();
35025 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
35030 * @class Roo.bootstrap.panel.Nest
35031 * @extends Roo.bootstrap.panel.Content
35033 * Create a new Panel, that can contain a layout.Border.
35036 * @param {Roo.BorderLayout} layout The layout for this panel
35037 * @param {String/Object} config A string to set only the title or a config object
35039 Roo.bootstrap.panel.Nest = function(config)
35041 // construct with only one argument..
35042 /* FIXME - implement nicer consturctors
35043 if (layout.layout) {
35045 layout = config.layout;
35046 delete config.layout;
35048 if (layout.xtype && !layout.getEl) {
35049 // then layout needs constructing..
35050 layout = Roo.factory(layout, Roo);
35054 config.el = config.layout.getEl();
35056 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35058 config.layout.monitorWindowResize = false; // turn off autosizing
35059 this.layout = config.layout;
35060 this.layout.getEl().addClass("roo-layout-nested-layout");
35067 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35069 setSize : function(width, height){
35070 if(!this.ignoreResize(width, height)){
35071 var size = this.adjustForComponents(width, height);
35072 var el = this.layout.getEl();
35073 if (size.height < 1) {
35074 el.setWidth(size.width);
35076 el.setSize(size.width, size.height);
35078 var touch = el.dom.offsetWidth;
35079 this.layout.layout();
35080 // ie requires a double layout on the first pass
35081 if(Roo.isIE && !this.initialized){
35082 this.initialized = true;
35083 this.layout.layout();
35088 // activate all subpanels if not currently active..
35090 setActiveState : function(active){
35091 this.active = active;
35092 this.setActiveClass(active);
35095 this.fireEvent("deactivate", this);
35099 this.fireEvent("activate", this);
35100 // not sure if this should happen before or after..
35101 if (!this.layout) {
35102 return; // should not happen..
35105 for (var r in this.layout.regions) {
35106 reg = this.layout.getRegion(r);
35107 if (reg.getActivePanel()) {
35108 //reg.showPanel(reg.getActivePanel()); // force it to activate..
35109 reg.setActivePanel(reg.getActivePanel());
35112 if (!reg.panels.length) {
35115 reg.showPanel(reg.getPanel(0));
35124 * Returns the nested BorderLayout for this panel
35125 * @return {Roo.BorderLayout}
35127 getLayout : function(){
35128 return this.layout;
35132 * Adds a xtype elements to the layout of the nested panel
35136 xtype : 'ContentPanel',
35143 xtype : 'NestedLayoutPanel',
35149 items : [ ... list of content panels or nested layout panels.. ]
35153 * @param {Object} cfg Xtype definition of item to add.
35155 addxtype : function(cfg) {
35156 return this.layout.addxtype(cfg);
35161 * Ext JS Library 1.1.1
35162 * Copyright(c) 2006-2007, Ext JS, LLC.
35164 * Originally Released Under LGPL - original licence link has changed is not relivant.
35167 * <script type="text/javascript">
35170 * @class Roo.TabPanel
35171 * @extends Roo.util.Observable
35172 * A lightweight tab container.
35176 // basic tabs 1, built from existing content
35177 var tabs = new Roo.TabPanel("tabs1");
35178 tabs.addTab("script", "View Script");
35179 tabs.addTab("markup", "View Markup");
35180 tabs.activate("script");
35182 // more advanced tabs, built from javascript
35183 var jtabs = new Roo.TabPanel("jtabs");
35184 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35186 // set up the UpdateManager
35187 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35188 var updater = tab2.getUpdateManager();
35189 updater.setDefaultUrl("ajax1.htm");
35190 tab2.on('activate', updater.refresh, updater, true);
35192 // Use setUrl for Ajax loading
35193 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35194 tab3.setUrl("ajax2.htm", null, true);
35197 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35200 jtabs.activate("jtabs-1");
35203 * Create a new TabPanel.
35204 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35205 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35207 Roo.bootstrap.panel.Tabs = function(config){
35209 * The container element for this TabPanel.
35210 * @type Roo.Element
35212 this.el = Roo.get(config.el);
35215 if(typeof config == "boolean"){
35216 this.tabPosition = config ? "bottom" : "top";
35218 Roo.apply(this, config);
35222 if(this.tabPosition == "bottom"){
35223 this.bodyEl = Roo.get(this.createBody(this.el.dom));
35224 this.el.addClass("roo-tabs-bottom");
35226 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35227 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35228 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35230 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35232 if(this.tabPosition != "bottom"){
35233 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35234 * @type Roo.Element
35236 this.bodyEl = Roo.get(this.createBody(this.el.dom));
35237 this.el.addClass("roo-tabs-top");
35241 this.bodyEl.setStyle("position", "relative");
35243 this.active = null;
35244 this.activateDelegate = this.activate.createDelegate(this);
35249 * Fires when the active tab changes
35250 * @param {Roo.TabPanel} this
35251 * @param {Roo.TabPanelItem} activePanel The new active tab
35255 * @event beforetabchange
35256 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35257 * @param {Roo.TabPanel} this
35258 * @param {Object} e Set cancel to true on this object to cancel the tab change
35259 * @param {Roo.TabPanelItem} tab The tab being changed to
35261 "beforetabchange" : true
35264 Roo.EventManager.onWindowResize(this.onResize, this);
35265 this.cpad = this.el.getPadding("lr");
35266 this.hiddenCount = 0;
35269 // toolbar on the tabbar support...
35270 if (this.toolbar) {
35271 alert("no toolbar support yet");
35272 this.toolbar = false;
35274 var tcfg = this.toolbar;
35275 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
35276 this.toolbar = new Roo.Toolbar(tcfg);
35277 if (Roo.isSafari) {
35278 var tbl = tcfg.container.child('table', true);
35279 tbl.setAttribute('width', '100%');
35287 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35290 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35292 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35294 tabPosition : "top",
35296 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35298 currentTabWidth : 0,
35300 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35304 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35308 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35310 preferredTabWidth : 175,
35312 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35314 resizeTabs : false,
35316 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35318 monitorResize : true,
35320 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
35325 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35326 * @param {String} id The id of the div to use <b>or create</b>
35327 * @param {String} text The text for the tab
35328 * @param {String} content (optional) Content to put in the TabPanelItem body
35329 * @param {Boolean} closable (optional) True to create a close icon on the tab
35330 * @return {Roo.TabPanelItem} The created TabPanelItem
35332 addTab : function(id, text, content, closable, tpl)
35334 var item = new Roo.bootstrap.panel.TabItem({
35338 closable : closable,
35341 this.addTabItem(item);
35343 item.setContent(content);
35349 * Returns the {@link Roo.TabPanelItem} with the specified id/index
35350 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35351 * @return {Roo.TabPanelItem}
35353 getTab : function(id){
35354 return this.items[id];
35358 * Hides the {@link Roo.TabPanelItem} with the specified id/index
35359 * @param {String/Number} id The id or index of the TabPanelItem to hide.
35361 hideTab : function(id){
35362 var t = this.items[id];
35365 this.hiddenCount++;
35366 this.autoSizeTabs();
35371 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35372 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35374 unhideTab : function(id){
35375 var t = this.items[id];
35377 t.setHidden(false);
35378 this.hiddenCount--;
35379 this.autoSizeTabs();
35384 * Adds an existing {@link Roo.TabPanelItem}.
35385 * @param {Roo.TabPanelItem} item The TabPanelItem to add
35387 addTabItem : function(item){
35388 this.items[item.id] = item;
35389 this.items.push(item);
35390 // if(this.resizeTabs){
35391 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35392 // this.autoSizeTabs();
35394 // item.autoSize();
35399 * Removes a {@link Roo.TabPanelItem}.
35400 * @param {String/Number} id The id or index of the TabPanelItem to remove.
35402 removeTab : function(id){
35403 var items = this.items;
35404 var tab = items[id];
35405 if(!tab) { return; }
35406 var index = items.indexOf(tab);
35407 if(this.active == tab && items.length > 1){
35408 var newTab = this.getNextAvailable(index);
35413 this.stripEl.dom.removeChild(tab.pnode.dom);
35414 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
35415 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
35417 items.splice(index, 1);
35418 delete this.items[tab.id];
35419 tab.fireEvent("close", tab);
35420 tab.purgeListeners();
35421 this.autoSizeTabs();
35424 getNextAvailable : function(start){
35425 var items = this.items;
35427 // look for a next tab that will slide over to
35428 // replace the one being removed
35429 while(index < items.length){
35430 var item = items[++index];
35431 if(item && !item.isHidden()){
35435 // if one isn't found select the previous tab (on the left)
35438 var item = items[--index];
35439 if(item && !item.isHidden()){
35447 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
35448 * @param {String/Number} id The id or index of the TabPanelItem to disable.
35450 disableTab : function(id){
35451 var tab = this.items[id];
35452 if(tab && this.active != tab){
35458 * Enables a {@link Roo.TabPanelItem} that is disabled.
35459 * @param {String/Number} id The id or index of the TabPanelItem to enable.
35461 enableTab : function(id){
35462 var tab = this.items[id];
35467 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
35468 * @param {String/Number} id The id or index of the TabPanelItem to activate.
35469 * @return {Roo.TabPanelItem} The TabPanelItem.
35471 activate : function(id){
35472 var tab = this.items[id];
35476 if(tab == this.active || tab.disabled){
35480 this.fireEvent("beforetabchange", this, e, tab);
35481 if(e.cancel !== true && !tab.disabled){
35483 this.active.hide();
35485 this.active = this.items[id];
35486 this.active.show();
35487 this.fireEvent("tabchange", this, this.active);
35493 * Gets the active {@link Roo.TabPanelItem}.
35494 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35496 getActiveTab : function(){
35497 return this.active;
35501 * Updates the tab body element to fit the height of the container element
35502 * for overflow scrolling
35503 * @param {Number} targetHeight (optional) Override the starting height from the elements height
35505 syncHeight : function(targetHeight){
35506 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35507 var bm = this.bodyEl.getMargins();
35508 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35509 this.bodyEl.setHeight(newHeight);
35513 onResize : function(){
35514 if(this.monitorResize){
35515 this.autoSizeTabs();
35520 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35522 beginUpdate : function(){
35523 this.updating = true;
35527 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35529 endUpdate : function(){
35530 this.updating = false;
35531 this.autoSizeTabs();
35535 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35537 autoSizeTabs : function(){
35538 var count = this.items.length;
35539 var vcount = count - this.hiddenCount;
35540 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35543 var w = Math.max(this.el.getWidth() - this.cpad, 10);
35544 var availWidth = Math.floor(w / vcount);
35545 var b = this.stripBody;
35546 if(b.getWidth() > w){
35547 var tabs = this.items;
35548 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35549 if(availWidth < this.minTabWidth){
35550 /*if(!this.sleft){ // incomplete scrolling code
35551 this.createScrollButtons();
35554 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35557 if(this.currentTabWidth < this.preferredTabWidth){
35558 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35564 * Returns the number of tabs in this TabPanel.
35567 getCount : function(){
35568 return this.items.length;
35572 * Resizes all the tabs to the passed width
35573 * @param {Number} The new width
35575 setTabWidth : function(width){
35576 this.currentTabWidth = width;
35577 for(var i = 0, len = this.items.length; i < len; i++) {
35578 if(!this.items[i].isHidden()) {
35579 this.items[i].setWidth(width);
35585 * Destroys this TabPanel
35586 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
35588 destroy : function(removeEl){
35589 Roo.EventManager.removeResizeListener(this.onResize, this);
35590 for(var i = 0, len = this.items.length; i < len; i++){
35591 this.items[i].purgeListeners();
35593 if(removeEl === true){
35594 this.el.update("");
35599 createStrip : function(container)
35601 var strip = document.createElement("nav");
35602 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
35603 container.appendChild(strip);
35607 createStripList : function(strip)
35609 // div wrapper for retard IE
35610 // returns the "tr" element.
35611 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
35612 //'<div class="x-tabs-strip-wrap">'+
35613 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
35614 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
35615 return strip.firstChild; //.firstChild.firstChild.firstChild;
35617 createBody : function(container)
35619 var body = document.createElement("div");
35620 Roo.id(body, "tab-body");
35621 //Roo.fly(body).addClass("x-tabs-body");
35622 Roo.fly(body).addClass("tab-content");
35623 container.appendChild(body);
35626 createItemBody :function(bodyEl, id){
35627 var body = Roo.getDom(id);
35629 body = document.createElement("div");
35632 //Roo.fly(body).addClass("x-tabs-item-body");
35633 Roo.fly(body).addClass("tab-pane");
35634 bodyEl.insertBefore(body, bodyEl.firstChild);
35638 createStripElements : function(stripEl, text, closable, tpl)
35640 var td = document.createElement("li"); // was td..
35643 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
35646 stripEl.appendChild(td);
35648 td.className = "x-tabs-closable";
35649 if(!this.closeTpl){
35650 this.closeTpl = new Roo.Template(
35651 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35652 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
35653 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
35656 var el = this.closeTpl.overwrite(td, {"text": text});
35657 var close = el.getElementsByTagName("div")[0];
35658 var inner = el.getElementsByTagName("em")[0];
35659 return {"el": el, "close": close, "inner": inner};
35662 // not sure what this is..
35663 // if(!this.tabTpl){
35664 //this.tabTpl = new Roo.Template(
35665 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35666 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
35668 // this.tabTpl = new Roo.Template(
35669 // '<a href="#">' +
35670 // '<span unselectable="on"' +
35671 // (this.disableTooltips ? '' : ' title="{text}"') +
35672 // ' >{text}</span></a>'
35678 var template = tpl || this.tabTpl || false;
35682 template = new Roo.Template(
35684 '<span unselectable="on"' +
35685 (this.disableTooltips ? '' : ' title="{text}"') +
35686 ' >{text}</span></a>'
35690 switch (typeof(template)) {
35694 template = new Roo.Template(template);
35700 var el = template.overwrite(td, {"text": text});
35702 var inner = el.getElementsByTagName("span")[0];
35704 return {"el": el, "inner": inner};
35712 * @class Roo.TabPanelItem
35713 * @extends Roo.util.Observable
35714 * Represents an individual item (tab plus body) in a TabPanel.
35715 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35716 * @param {String} id The id of this TabPanelItem
35717 * @param {String} text The text for the tab of this TabPanelItem
35718 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35720 Roo.bootstrap.panel.TabItem = function(config){
35722 * The {@link Roo.TabPanel} this TabPanelItem belongs to
35723 * @type Roo.TabPanel
35725 this.tabPanel = config.panel;
35727 * The id for this TabPanelItem
35730 this.id = config.id;
35732 this.disabled = false;
35734 this.text = config.text;
35736 this.loaded = false;
35737 this.closable = config.closable;
35740 * The body element for this TabPanelItem.
35741 * @type Roo.Element
35743 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35744 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35745 this.bodyEl.setStyle("display", "block");
35746 this.bodyEl.setStyle("zoom", "1");
35747 //this.hideAction();
35749 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
35751 this.el = Roo.get(els.el);
35752 this.inner = Roo.get(els.inner, true);
35753 this.textEl = Roo.get(this.el.dom.firstChild, true);
35754 this.pnode = Roo.get(els.el.parentNode, true);
35755 this.el.on("mousedown", this.onTabMouseDown, this);
35756 this.el.on("click", this.onTabClick, this);
35758 if(config.closable){
35759 var c = Roo.get(els.close, true);
35760 c.dom.title = this.closeText;
35761 c.addClassOnOver("close-over");
35762 c.on("click", this.closeClick, this);
35768 * Fires when this tab becomes the active tab.
35769 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35770 * @param {Roo.TabPanelItem} this
35774 * @event beforeclose
35775 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35776 * @param {Roo.TabPanelItem} this
35777 * @param {Object} e Set cancel to true on this object to cancel the close.
35779 "beforeclose": true,
35782 * Fires when this tab is closed.
35783 * @param {Roo.TabPanelItem} this
35787 * @event deactivate
35788 * Fires when this tab is no longer the active tab.
35789 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35790 * @param {Roo.TabPanelItem} this
35792 "deactivate" : true
35794 this.hidden = false;
35796 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35799 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35801 purgeListeners : function(){
35802 Roo.util.Observable.prototype.purgeListeners.call(this);
35803 this.el.removeAllListeners();
35806 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35809 this.pnode.addClass("active");
35812 this.tabPanel.stripWrap.repaint();
35814 this.fireEvent("activate", this.tabPanel, this);
35818 * Returns true if this tab is the active tab.
35819 * @return {Boolean}
35821 isActive : function(){
35822 return this.tabPanel.getActiveTab() == this;
35826 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35829 this.pnode.removeClass("active");
35831 this.fireEvent("deactivate", this.tabPanel, this);
35834 hideAction : function(){
35835 this.bodyEl.hide();
35836 this.bodyEl.setStyle("position", "absolute");
35837 this.bodyEl.setLeft("-20000px");
35838 this.bodyEl.setTop("-20000px");
35841 showAction : function(){
35842 this.bodyEl.setStyle("position", "relative");
35843 this.bodyEl.setTop("");
35844 this.bodyEl.setLeft("");
35845 this.bodyEl.show();
35849 * Set the tooltip for the tab.
35850 * @param {String} tooltip The tab's tooltip
35852 setTooltip : function(text){
35853 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35854 this.textEl.dom.qtip = text;
35855 this.textEl.dom.removeAttribute('title');
35857 this.textEl.dom.title = text;
35861 onTabClick : function(e){
35862 e.preventDefault();
35863 this.tabPanel.activate(this.id);
35866 onTabMouseDown : function(e){
35867 e.preventDefault();
35868 this.tabPanel.activate(this.id);
35871 getWidth : function(){
35872 return this.inner.getWidth();
35875 setWidth : function(width){
35876 var iwidth = width - this.pnode.getPadding("lr");
35877 this.inner.setWidth(iwidth);
35878 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35879 this.pnode.setWidth(width);
35883 * Show or hide the tab
35884 * @param {Boolean} hidden True to hide or false to show.
35886 setHidden : function(hidden){
35887 this.hidden = hidden;
35888 this.pnode.setStyle("display", hidden ? "none" : "");
35892 * Returns true if this tab is "hidden"
35893 * @return {Boolean}
35895 isHidden : function(){
35896 return this.hidden;
35900 * Returns the text for this tab
35903 getText : function(){
35907 autoSize : function(){
35908 //this.el.beginMeasure();
35909 this.textEl.setWidth(1);
35911 * #2804 [new] Tabs in Roojs
35912 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35914 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35915 //this.el.endMeasure();
35919 * Sets the text for the tab (Note: this also sets the tooltip text)
35920 * @param {String} text The tab's text and tooltip
35922 setText : function(text){
35924 this.textEl.update(text);
35925 this.setTooltip(text);
35926 //if(!this.tabPanel.resizeTabs){
35927 // this.autoSize();
35931 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35933 activate : function(){
35934 this.tabPanel.activate(this.id);
35938 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35940 disable : function(){
35941 if(this.tabPanel.active != this){
35942 this.disabled = true;
35943 this.pnode.addClass("disabled");
35948 * Enables this TabPanelItem if it was previously disabled.
35950 enable : function(){
35951 this.disabled = false;
35952 this.pnode.removeClass("disabled");
35956 * Sets the content for this TabPanelItem.
35957 * @param {String} content The content
35958 * @param {Boolean} loadScripts true to look for and load scripts
35960 setContent : function(content, loadScripts){
35961 this.bodyEl.update(content, loadScripts);
35965 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35966 * @return {Roo.UpdateManager} The UpdateManager
35968 getUpdateManager : function(){
35969 return this.bodyEl.getUpdateManager();
35973 * Set a URL to be used to load the content for this TabPanelItem.
35974 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35975 * @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)
35976 * @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)
35977 * @return {Roo.UpdateManager} The UpdateManager
35979 setUrl : function(url, params, loadOnce){
35980 if(this.refreshDelegate){
35981 this.un('activate', this.refreshDelegate);
35983 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35984 this.on("activate", this.refreshDelegate);
35985 return this.bodyEl.getUpdateManager();
35989 _handleRefresh : function(url, params, loadOnce){
35990 if(!loadOnce || !this.loaded){
35991 var updater = this.bodyEl.getUpdateManager();
35992 updater.update(url, params, this._setLoaded.createDelegate(this));
35997 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
35998 * Will fail silently if the setUrl method has not been called.
35999 * This does not activate the panel, just updates its content.
36001 refresh : function(){
36002 if(this.refreshDelegate){
36003 this.loaded = false;
36004 this.refreshDelegate();
36009 _setLoaded : function(){
36010 this.loaded = true;
36014 closeClick : function(e){
36017 this.fireEvent("beforeclose", this, o);
36018 if(o.cancel !== true){
36019 this.tabPanel.removeTab(this.id);
36023 * The text displayed in the tooltip for the close icon.
36026 closeText : "Close this tab"