4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
105 cfg.id = this.id || Roo.id();
107 // fill in the extra attributes
108 if (this.xattr && typeof(this.xattr) =='object') {
109 for (var i in this.xattr) {
110 cfg[i] = this.xattr[i];
115 cfg.dataId = this.dataId;
119 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
122 if (this.style) { // fixme needs to support more complex style data.
123 cfg.style = this.style;
127 cfg.name = this.name;
130 this.el = ct.createChild(cfg, position);
133 this.tooltipEl().attr('tooltip', this.tooltip);
136 if(this.tabIndex !== undefined){
137 this.el.dom.setAttribute('tabIndex', this.tabIndex);
144 * Fetch the element to add children to
145 * @return {Roo.Element} defaults to this.el
147 getChildContainer : function()
152 * Fetch the element to display the tooltip on.
153 * @return {Roo.Element} defaults to this.el
155 tooltipEl : function()
160 addxtype : function(tree,cntr)
164 cn = Roo.factory(tree);
165 //Roo.log(['addxtype', cn]);
167 cn.parentType = this.xtype; //??
168 cn.parentId = this.id;
170 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171 if (typeof(cn.container_method) == 'string') {
172 cntr = cn.container_method;
176 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
178 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
180 var build_from_html = Roo.XComponent.build_from_html;
182 var is_body = (tree.xtype == 'Body') ;
184 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186 var self_cntr_el = Roo.get(this[cntr](false));
188 // do not try and build conditional elements
189 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
193 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195 return this.addxtypeChild(tree,cntr, is_body);
198 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
201 return this.addxtypeChild(Roo.apply({}, tree),cntr);
204 Roo.log('skipping render');
210 if (!build_from_html) {
214 // this i think handles overlaying multiple children of the same type
215 // with the sam eelement.. - which might be buggy..
217 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
223 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
227 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
234 addxtypeChild : function (tree, cntr, is_body)
236 Roo.debug && Roo.log('addxtypeChild:' + cntr);
238 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
241 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242 (typeof(tree['flexy:foreach']) != 'undefined');
246 skip_children = false;
247 // render the element if it's not BODY.
250 cn = Roo.factory(tree);
252 cn.parentType = this.xtype; //??
253 cn.parentId = this.id;
255 var build_from_html = Roo.XComponent.build_from_html;
258 // does the container contain child eleemnts with 'xtype' attributes.
259 // that match this xtype..
260 // note - when we render we create these as well..
261 // so we should check to see if body has xtype set.
262 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
264 var self_cntr_el = Roo.get(this[cntr](false));
265 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
267 //Roo.log(Roo.XComponent.build_from_html);
268 //Roo.log("got echild:");
271 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272 // and are not displayed -this causes this to use up the wrong element when matching.
273 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
276 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
283 //echild.dom.removeAttribute('xtype');
285 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286 Roo.debug && Roo.log(self_cntr_el);
287 Roo.debug && Roo.log(echild);
288 Roo.debug && Roo.log(cn);
294 // if object has flexy:if - then it may or may not be rendered.
295 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
296 // skip a flexy if element.
297 Roo.debug && Roo.log('skipping render');
298 Roo.debug && Roo.log(tree);
300 Roo.debug && Roo.log('skipping all children');
301 skip_children = true;
306 // actually if flexy:foreach is found, we really want to create
307 // multiple copies here...
309 //Roo.log(this[cntr]());
310 // some elements do not have render methods.. like the layouts...
311 cn.render && cn.render(this[cntr](true));
313 // then add the element..
321 if (typeof (tree.menu) != 'undefined') {
322 tree.menu.parentType = cn.xtype;
323 tree.menu.triggerEl = cn.el;
324 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
328 if (!tree.items || !tree.items.length) {
330 //Roo.log(["no children", this]);
335 var items = tree.items;
338 //Roo.log(items.length);
340 if (!skip_children) {
341 for(var i =0;i < items.length;i++) {
342 // Roo.log(['add child', items[i]]);
343 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
349 //Roo.log("fire childrenrendered");
351 cn.fireEvent('childrenrendered', this);
356 * Show a component - removes 'hidden' class
361 this.el.removeClass('hidden');
365 * Hide a component - adds 'hidden' class
369 if (this.el && !this.el.hasClass('hidden')) {
370 this.el.addClass('hidden');
384 * @class Roo.bootstrap.Body
385 * @extends Roo.bootstrap.Component
386 * Bootstrap Body class
390 * @param {Object} config The config object
393 Roo.bootstrap.Body = function(config){
395 config = config || {};
397 Roo.bootstrap.Body.superclass.constructor.call(this, config);
398 this.el = Roo.get(config.el ? config.el : document.body );
399 if (this.cls && this.cls.length) {
400 Roo.get(document.body).addClass(this.cls);
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
406 is_body : true,// just to make sure it's constructed?
411 onRender : function(ct, position)
413 /* Roo.log("Roo.bootstrap.Body - onRender");
414 if (this.cls && this.cls.length) {
415 Roo.get(document.body).addClass(this.cls);
434 * @class Roo.bootstrap.ButtonGroup
435 * @extends Roo.bootstrap.Component
436 * Bootstrap ButtonGroup class
437 * @cfg {String} size lg | sm | xs (default empty normal)
438 * @cfg {String} align vertical | justified (default none)
439 * @cfg {String} direction up | down (default down)
440 * @cfg {Boolean} toolbar false | true
441 * @cfg {Boolean} btn true | false
446 * @param {Object} config The config object
449 Roo.bootstrap.ButtonGroup = function(config){
450 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
461 getAutoCreate : function(){
467 cfg.html = this.html || cfg.html;
478 if (['vertical','justified'].indexOf(this.align)!==-1) {
479 cfg.cls = 'btn-group-' + this.align;
481 if (this.align == 'justified') {
482 console.log(this.items);
486 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487 cfg.cls += ' btn-group-' + this.size;
490 if (this.direction == 'up') {
491 cfg.cls += ' dropup' ;
507 * @class Roo.bootstrap.Button
508 * @extends Roo.bootstrap.Component
509 * Bootstrap Button class
510 * @cfg {String} html The button content
511 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
512 * @cfg {String} size ( lg | sm | xs)
513 * @cfg {String} tag ( a | input | submit)
514 * @cfg {String} href empty or href
515 * @cfg {Boolean} disabled default false;
516 * @cfg {Boolean} isClose default false;
517 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
518 * @cfg {String} badge text for badge
519 * @cfg {String} theme default
520 * @cfg {Boolean} inverse
521 * @cfg {Boolean} toggle
522 * @cfg {String} ontext text for on toggle state
523 * @cfg {String} offtext text for off toggle state
524 * @cfg {Boolean} defaulton
525 * @cfg {Boolean} preventDefault default true
526 * @cfg {Boolean} removeClass remove the standard class..
527 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
530 * Create a new button
531 * @param {Object} config The config object
535 Roo.bootstrap.Button = function(config){
536 Roo.bootstrap.Button.superclass.constructor.call(this, config);
541 * When a butotn is pressed
542 * @param {Roo.bootstrap.Button} this
543 * @param {Roo.EventObject} e
548 * After the button has been toggles
549 * @param {Roo.EventObject} e
550 * @param {boolean} pressed (also available as button.pressed)
556 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
574 preventDefault: true,
583 getAutoCreate : function(){
591 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
592 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
597 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
599 if (this.toggle == true) {
602 cls: 'slider-frame roo-button',
607 'data-off-text':'OFF',
608 cls: 'slider-button',
614 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
615 cfg.cls += ' '+this.weight;
624 cfg["aria-hidden"] = true;
626 cfg.html = "×";
632 if (this.theme==='default') {
633 cfg.cls = 'btn roo-button';
635 //if (this.parentType != 'Navbar') {
636 this.weight = this.weight.length ? this.weight : 'default';
638 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
640 cfg.cls += ' btn-' + this.weight;
642 } else if (this.theme==='glow') {
645 cfg.cls = 'btn-glow roo-button';
647 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
649 cfg.cls += ' ' + this.weight;
655 this.cls += ' inverse';
660 cfg.cls += ' active';
664 cfg.disabled = 'disabled';
668 Roo.log('changing to ul' );
670 this.glyphicon = 'caret';
673 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
675 //gsRoo.log(this.parentType);
676 if (this.parentType === 'Navbar' && !this.parent().bar) {
677 Roo.log('changing to li?');
686 href : this.href || '#'
689 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
690 cfg.cls += ' dropdown';
697 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
699 if (this.glyphicon) {
700 cfg.html = ' ' + cfg.html;
705 cls: 'glyphicon glyphicon-' + this.glyphicon
715 // cfg.cls='btn roo-button';
719 var value = cfg.html;
724 cls: 'glyphicon glyphicon-' + this.glyphicon,
743 cfg.cls += ' dropdown';
744 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
747 if (cfg.tag !== 'a' && this.href !== '') {
748 throw "Tag must be a to set href.";
749 } else if (this.href.length > 0) {
750 cfg.href = this.href;
753 if(this.removeClass){
758 cfg.target = this.target;
763 initEvents: function() {
764 // Roo.log('init events?');
765 // Roo.log(this.el.dom);
768 if (typeof (this.menu) != 'undefined') {
769 this.menu.parentType = this.xtype;
770 this.menu.triggerEl = this.el;
771 this.addxtype(Roo.apply({}, this.menu));
775 if (this.el.hasClass('roo-button')) {
776 this.el.on('click', this.onClick, this);
778 this.el.select('.roo-button').on('click', this.onClick, this);
781 if(this.removeClass){
782 this.el.on('click', this.onClick, this);
785 this.el.enableDisplayMode();
788 onClick : function(e)
795 Roo.log('button on click ');
796 if(this.preventDefault){
799 if (this.pressed === true || this.pressed === false) {
800 this.pressed = !this.pressed;
801 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
802 this.fireEvent('toggle', this, e, this.pressed);
806 this.fireEvent('click', this, e);
810 * Enables this button
814 this.disabled = false;
815 this.el.removeClass('disabled');
819 * Disable this button
823 this.disabled = true;
824 this.el.addClass('disabled');
827 * sets the active state on/off,
828 * @param {Boolean} state (optional) Force a particular state
830 setActive : function(v) {
832 this.el[v ? 'addClass' : 'removeClass']('active');
835 * toggles the current active state
837 toggleActive : function()
839 var active = this.el.hasClass('active');
840 this.setActive(!active);
844 setText : function(str)
846 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
850 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
873 * @class Roo.bootstrap.Column
874 * @extends Roo.bootstrap.Component
875 * Bootstrap Column class
876 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
877 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
878 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
879 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
880 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
881 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
882 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
883 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
886 * @cfg {Boolean} hidden (true|false) hide the element
887 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
888 * @cfg {String} fa (ban|check|...) font awesome icon
889 * @cfg {Number} fasize (1|2|....) font awsome size
891 * @cfg {String} icon (info-sign|check|...) glyphicon name
893 * @cfg {String} html content of column.
896 * Create a new Column
897 * @param {Object} config The config object
900 Roo.bootstrap.Column = function(config){
901 Roo.bootstrap.Column.superclass.constructor.call(this, config);
904 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
922 getAutoCreate : function(){
923 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
931 ['xs','sm','md','lg'].map(function(size){
932 //Roo.log( size + ':' + settings[size]);
934 if (settings[size+'off'] !== false) {
935 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
938 if (settings[size] === false) {
942 if (!settings[size]) { // 0 = hidden
943 cfg.cls += ' hidden-' + size;
946 cfg.cls += ' col-' + size + '-' + settings[size];
951 cfg.cls += ' hidden';
954 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
955 cfg.cls +=' alert alert-' + this.alert;
959 if (this.html.length) {
960 cfg.html = this.html;
964 if (this.fasize > 1) {
965 fasize = ' fa-' + this.fasize + 'x';
967 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
972 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
991 * @class Roo.bootstrap.Container
992 * @extends Roo.bootstrap.Component
993 * Bootstrap Container class
994 * @cfg {Boolean} jumbotron is it a jumbotron element
995 * @cfg {String} html content of element
996 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
997 * @cfg {String} panel (primary|success|info|warning|danger) render as panel - type - primary/success.....
998 * @cfg {String} header content of header (for panel)
999 * @cfg {String} footer content of footer (for panel)
1000 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1001 * @cfg {String} tag (header|aside|section) type of HTML tag.
1002 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1003 * @cfg {String} fa font awesome icon
1004 * @cfg {String} icon (info-sign|check|...) glyphicon name
1005 * @cfg {Boolean} hidden (true|false) hide the element
1006 * @cfg {Boolean} expandable (true|false) default false
1007 * @cfg {Boolean} expanded (true|false) default true
1008 * @cfg {String} rheader contet on the right of header
1009 * @cfg {Boolean} clickable (true|false) default false
1013 * Create a new Container
1014 * @param {Object} config The config object
1017 Roo.bootstrap.Container = function(config){
1018 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1024 * After the panel has been expand
1026 * @param {Roo.bootstrap.Container} this
1031 * After the panel has been collapsed
1033 * @param {Roo.bootstrap.Container} this
1038 * When a element is chick
1039 * @param {Roo.bootstrap.Container} this
1040 * @param {Roo.EventObject} e
1046 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1064 getChildContainer : function() {
1070 if (this.panel.length) {
1071 return this.el.select('.panel-body',true).first();
1078 getAutoCreate : function(){
1081 tag : this.tag || 'div',
1085 if (this.jumbotron) {
1086 cfg.cls = 'jumbotron';
1091 // - this is applied by the parent..
1093 // cfg.cls = this.cls + '';
1096 if (this.sticky.length) {
1098 var bd = Roo.get(document.body);
1099 if (!bd.hasClass('bootstrap-sticky')) {
1100 bd.addClass('bootstrap-sticky');
1101 Roo.select('html',true).setStyle('height', '100%');
1104 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1108 if (this.well.length) {
1109 switch (this.well) {
1112 cfg.cls +=' well well-' +this.well;
1121 cfg.cls += ' hidden';
1125 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1126 cfg.cls +=' alert alert-' + this.alert;
1131 if (this.panel.length) {
1132 cfg.cls += ' panel panel-' + this.panel;
1134 if (this.header.length) {
1138 if(this.expandable){
1140 cfg.cls = cfg.cls + ' expandable';
1144 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1152 cls : 'panel-title',
1153 html : (this.expandable ? ' ' : '') + this.header
1157 cls: 'panel-header-right',
1163 cls : 'panel-heading',
1164 style : this.expandable ? 'cursor: pointer' : '',
1172 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1177 if (this.footer.length) {
1179 cls : 'panel-footer',
1188 body.html = this.html || cfg.html;
1189 // prefix with the icons..
1191 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1194 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1199 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1200 cfg.cls = 'container';
1206 initEvents: function()
1208 if(this.expandable){
1209 var headerEl = this.headerEl();
1212 headerEl.on('click', this.onToggleClick, this);
1217 this.el.on('click', this.onClick, this);
1222 onToggleClick : function()
1224 var headerEl = this.headerEl();
1240 if(this.fireEvent('expand', this)) {
1242 this.expanded = true;
1244 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1246 this.el.select('.panel-body',true).first().removeClass('hide');
1248 var toggleEl = this.toggleEl();
1254 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1259 collapse : function()
1261 if(this.fireEvent('collapse', this)) {
1263 this.expanded = false;
1265 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1266 this.el.select('.panel-body',true).first().addClass('hide');
1268 var toggleEl = this.toggleEl();
1274 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1278 toggleEl : function()
1280 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1284 return this.el.select('.panel-heading .fa',true).first();
1287 headerEl : function()
1289 if(!this.el || !this.panel.length || !this.header.length){
1293 return this.el.select('.panel-heading',true).first()
1296 titleEl : function()
1298 if(!this.el || !this.panel.length || !this.header.length){
1302 return this.el.select('.panel-title',true).first();
1305 setTitle : function(v)
1307 var titleEl = this.titleEl();
1313 titleEl.dom.innerHTML = v;
1316 getTitle : function()
1319 var titleEl = this.titleEl();
1325 return titleEl.dom.innerHTML;
1328 setRightTitle : function(v)
1330 var t = this.el.select('.panel-header-right',true).first();
1336 t.dom.innerHTML = v;
1339 onClick : function(e)
1343 this.fireEvent('click', this, e);
1357 * @class Roo.bootstrap.Img
1358 * @extends Roo.bootstrap.Component
1359 * Bootstrap Img class
1360 * @cfg {Boolean} imgResponsive false | true
1361 * @cfg {String} border rounded | circle | thumbnail
1362 * @cfg {String} src image source
1363 * @cfg {String} alt image alternative text
1364 * @cfg {String} href a tag href
1365 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1366 * @cfg {String} xsUrl xs image source
1367 * @cfg {String} smUrl sm image source
1368 * @cfg {String} mdUrl md image source
1369 * @cfg {String} lgUrl lg image source
1372 * Create a new Input
1373 * @param {Object} config The config object
1376 Roo.bootstrap.Img = function(config){
1377 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1383 * The img click event for the img.
1384 * @param {Roo.EventObject} e
1390 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1392 imgResponsive: true,
1402 getAutoCreate : function()
1404 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1405 return this.createSingleImg();
1410 cls: 'roo-image-responsive-group',
1415 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1417 if(!_this[size + 'Url']){
1423 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1424 html: _this.html || cfg.html,
1425 src: _this[size + 'Url']
1428 img.cls += ' roo-image-responsive-' + size;
1430 var s = ['xs', 'sm', 'md', 'lg'];
1432 s.splice(s.indexOf(size), 1);
1434 Roo.each(s, function(ss){
1435 img.cls += ' hidden-' + ss;
1438 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1439 cfg.cls += ' img-' + _this.border;
1443 cfg.alt = _this.alt;
1456 a.target = _this.target;
1460 cfg.cn.push((_this.href) ? a : img);
1467 createSingleImg : function()
1471 cls: (this.imgResponsive) ? 'img-responsive' : '',
1473 src : 'about:blank' // just incase src get's set to undefined?!?
1476 cfg.html = this.html || cfg.html;
1478 cfg.src = this.src || cfg.src;
1480 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1481 cfg.cls += ' img-' + this.border;
1498 a.target = this.target;
1503 return (this.href) ? a : cfg;
1506 initEvents: function()
1509 this.el.on('click', this.onClick, this);
1514 onClick : function(e)
1516 Roo.log('img onclick');
1517 this.fireEvent('click', this, e);
1520 * Sets the url of the image - used to update it
1521 * @param {String} url the url of the image
1524 setSrc : function(url)
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 if (this.triggerEl) {
14261 this.triggerEl.on("click", this.showTouchView, this);
14265 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14266 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14268 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14270 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14271 this.store.on('load', this.onTouchViewLoad, this);
14272 this.store.on('loadexception', this.onTouchViewLoadException, this);
14274 if(this.hiddenName){
14276 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14278 this.hiddenField.dom.value =
14279 this.hiddenValue !== undefined ? this.hiddenValue :
14280 this.value !== undefined ? this.value : '';
14282 this.el.dom.removeAttribute('name');
14283 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14287 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14288 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14291 if(this.removable && !this.multiple){
14292 var close = this.closeTriggerEl();
14294 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14295 close.on('click', this.removeBtnClick, this, close);
14299 * fix the bug in Safari iOS8
14301 this.inputEl().on("focus", function(e){
14302 document.activeElement.blur();
14310 renderTouchView : function()
14312 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14313 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14315 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14316 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14318 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14319 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14320 this.touchViewBodyEl.setStyle('overflow', 'auto');
14322 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14323 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14325 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14326 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14330 showTouchView : function()
14336 this.touchViewHeaderEl.hide();
14338 if(this.modalTitle.length){
14339 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14340 this.touchViewHeaderEl.show();
14343 this.touchViewEl.show();
14345 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14346 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14347 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14349 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14351 if(this.modalTitle.length){
14352 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14355 this.touchViewBodyEl.setHeight(bodyHeight);
14359 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14361 this.touchViewEl.addClass('in');
14364 this.doTouchViewQuery();
14368 hideTouchView : function()
14370 this.touchViewEl.removeClass('in');
14374 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14376 this.touchViewEl.setStyle('display', 'none');
14381 setTouchViewValue : function()
14388 Roo.each(this.tickItems, function(o){
14393 this.hideTouchView();
14396 doTouchViewQuery : function()
14405 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14409 if(!this.alwaysQuery || this.mode == 'local'){
14410 this.onTouchViewLoad();
14417 onTouchViewBeforeLoad : function(combo,opts)
14423 onTouchViewLoad : function()
14425 if(this.store.getCount() < 1){
14426 this.onTouchViewEmptyResults();
14430 this.clearTouchView();
14432 var rawValue = this.getRawValue();
14434 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14436 this.tickItems = [];
14438 this.store.data.each(function(d, rowIndex){
14439 var row = this.touchViewListGroup.createChild(template);
14441 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14442 row.addClass(d.data.cls);
14445 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14448 html : d.data[this.displayField]
14451 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14452 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14455 row.removeClass('selected');
14456 if(!this.multiple && this.valueField &&
14457 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14460 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14461 row.addClass('selected');
14464 if(this.multiple && this.valueField &&
14465 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14469 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14470 this.tickItems.push(d.data);
14473 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14477 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14479 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14481 if(this.modalTitle.length){
14482 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14485 var listHeight = this.touchViewListGroup.getHeight();
14489 if(firstChecked && listHeight > bodyHeight){
14490 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14495 onTouchViewLoadException : function()
14497 this.hideTouchView();
14500 onTouchViewEmptyResults : function()
14502 this.clearTouchView();
14504 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14506 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14510 clearTouchView : function()
14512 this.touchViewListGroup.dom.innerHTML = '';
14515 onTouchViewClick : function(e, el, o)
14517 e.preventDefault();
14520 var rowIndex = o.rowIndex;
14522 var r = this.store.getAt(rowIndex);
14524 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14526 if(!this.multiple){
14527 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14528 c.dom.removeAttribute('checked');
14531 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14533 this.setFromData(r.data);
14535 var close = this.closeTriggerEl();
14541 this.hideTouchView();
14543 this.fireEvent('select', this, r, rowIndex);
14548 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14549 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14550 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14554 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14555 this.addItem(r.data);
14556 this.tickItems.push(r.data);
14560 getAutoCreateNativeIOS : function()
14563 cls: 'form-group' //input-group,
14568 cls : 'roo-ios-select'
14572 combobox.name = this.name;
14575 if (this.disabled) {
14576 combobox.disabled = true;
14579 var settings = this;
14581 ['xs','sm','md','lg'].map(function(size){
14582 if (settings[size]) {
14583 cfg.cls += ' col-' + size + '-' + settings[size];
14593 initIOSView : function()
14595 this.store.on('load', this.onIOSViewLoad, this);
14600 onIOSViewLoad : function()
14602 if(this.store.getCount() < 1){
14606 this.clearIOSView();
14608 if(this.allowBlank) {
14610 var default_text = '-- SELECT --';
14612 var opt = this.inputEl().createChild({
14615 html : default_text
14619 o[this.valueField] = 0;
14620 o[this.displayField] = default_text;
14622 this.ios_options.push({
14629 this.store.data.each(function(d, rowIndex){
14633 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14634 html = d.data[this.displayField];
14639 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14640 value = d.data[this.valueField];
14649 if(this.value == d.data[this.valueField]){
14650 option['selected'] = true;
14653 var opt = this.inputEl().createChild(option);
14655 this.ios_options.push({
14662 this.inputEl().on('change', function(){
14663 this.fireEvent('select', this);
14668 clearIOSView: function()
14670 this.inputEl().dom.innerHTML = '';
14672 this.ios_options = [];
14675 setIOSValue: function(v)
14679 if(!this.ios_options){
14683 Roo.each(this.ios_options, function(opts){
14685 opts.el.dom.removeAttribute('selected');
14687 if(opts.data[this.valueField] != v){
14691 opts.el.dom.setAttribute('selected', true);
14697 * @cfg {Boolean} grow
14701 * @cfg {Number} growMin
14705 * @cfg {Number} growMax
14714 Roo.apply(Roo.bootstrap.ComboBox, {
14718 cls: 'modal-header',
14740 cls: 'list-group-item',
14744 cls: 'roo-combobox-list-group-item-value'
14748 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14762 listItemCheckbox : {
14764 cls: 'list-group-item',
14768 cls: 'roo-combobox-list-group-item-value'
14772 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14788 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14793 cls: 'modal-footer',
14801 cls: 'col-xs-6 text-left',
14804 cls: 'btn btn-danger roo-touch-view-cancel',
14810 cls: 'col-xs-6 text-right',
14813 cls: 'btn btn-success roo-touch-view-ok',
14824 Roo.apply(Roo.bootstrap.ComboBox, {
14826 touchViewTemplate : {
14828 cls: 'modal fade roo-combobox-touch-view',
14832 cls: 'modal-dialog',
14833 style : 'position:fixed', // we have to fix position....
14837 cls: 'modal-content',
14839 Roo.bootstrap.ComboBox.header,
14840 Roo.bootstrap.ComboBox.body,
14841 Roo.bootstrap.ComboBox.footer
14850 * Ext JS Library 1.1.1
14851 * Copyright(c) 2006-2007, Ext JS, LLC.
14853 * Originally Released Under LGPL - original licence link has changed is not relivant.
14856 * <script type="text/javascript">
14861 * @extends Roo.util.Observable
14862 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14863 * This class also supports single and multi selection modes. <br>
14864 * Create a data model bound view:
14866 var store = new Roo.data.Store(...);
14868 var view = new Roo.View({
14870 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14872 singleSelect: true,
14873 selectedClass: "ydataview-selected",
14877 // listen for node click?
14878 view.on("click", function(vw, index, node, e){
14879 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14883 dataModel.load("foobar.xml");
14885 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14887 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14888 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14890 * Note: old style constructor is still suported (container, template, config)
14893 * Create a new View
14894 * @param {Object} config The config object
14897 Roo.View = function(config, depreciated_tpl, depreciated_config){
14899 this.parent = false;
14901 if (typeof(depreciated_tpl) == 'undefined') {
14902 // new way.. - universal constructor.
14903 Roo.apply(this, config);
14904 this.el = Roo.get(this.el);
14907 this.el = Roo.get(config);
14908 this.tpl = depreciated_tpl;
14909 Roo.apply(this, depreciated_config);
14911 this.wrapEl = this.el.wrap().wrap();
14912 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14915 if(typeof(this.tpl) == "string"){
14916 this.tpl = new Roo.Template(this.tpl);
14918 // support xtype ctors..
14919 this.tpl = new Roo.factory(this.tpl, Roo);
14923 this.tpl.compile();
14928 * @event beforeclick
14929 * Fires before a click is processed. Returns false to cancel the default action.
14930 * @param {Roo.View} this
14931 * @param {Number} index The index of the target node
14932 * @param {HTMLElement} node The target node
14933 * @param {Roo.EventObject} e The raw event object
14935 "beforeclick" : true,
14938 * Fires when a template node is clicked.
14939 * @param {Roo.View} this
14940 * @param {Number} index The index of the target node
14941 * @param {HTMLElement} node The target node
14942 * @param {Roo.EventObject} e The raw event object
14947 * Fires when a template node is double clicked.
14948 * @param {Roo.View} this
14949 * @param {Number} index The index of the target node
14950 * @param {HTMLElement} node The target node
14951 * @param {Roo.EventObject} e The raw event object
14955 * @event contextmenu
14956 * Fires when a template node is right clicked.
14957 * @param {Roo.View} this
14958 * @param {Number} index The index of the target node
14959 * @param {HTMLElement} node The target node
14960 * @param {Roo.EventObject} e The raw event object
14962 "contextmenu" : true,
14964 * @event selectionchange
14965 * Fires when the selected nodes change.
14966 * @param {Roo.View} this
14967 * @param {Array} selections Array of the selected nodes
14969 "selectionchange" : true,
14972 * @event beforeselect
14973 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14974 * @param {Roo.View} this
14975 * @param {HTMLElement} node The node to be selected
14976 * @param {Array} selections Array of currently selected nodes
14978 "beforeselect" : true,
14980 * @event preparedata
14981 * Fires on every row to render, to allow you to change the data.
14982 * @param {Roo.View} this
14983 * @param {Object} data to be rendered (change this)
14985 "preparedata" : true
14993 "click": this.onClick,
14994 "dblclick": this.onDblClick,
14995 "contextmenu": this.onContextMenu,
14999 this.selections = [];
15001 this.cmp = new Roo.CompositeElementLite([]);
15003 this.store = Roo.factory(this.store, Roo.data);
15004 this.setStore(this.store, true);
15007 if ( this.footer && this.footer.xtype) {
15009 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15011 this.footer.dataSource = this.store;
15012 this.footer.container = fctr;
15013 this.footer = Roo.factory(this.footer, Roo);
15014 fctr.insertFirst(this.el);
15016 // this is a bit insane - as the paging toolbar seems to detach the el..
15017 // dom.parentNode.parentNode.parentNode
15018 // they get detached?
15022 Roo.View.superclass.constructor.call(this);
15027 Roo.extend(Roo.View, Roo.util.Observable, {
15030 * @cfg {Roo.data.Store} store Data store to load data from.
15035 * @cfg {String|Roo.Element} el The container element.
15040 * @cfg {String|Roo.Template} tpl The template used by this View
15044 * @cfg {String} dataName the named area of the template to use as the data area
15045 * Works with domtemplates roo-name="name"
15049 * @cfg {String} selectedClass The css class to add to selected nodes
15051 selectedClass : "x-view-selected",
15053 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15058 * @cfg {String} text to display on mask (default Loading)
15062 * @cfg {Boolean} multiSelect Allow multiple selection
15064 multiSelect : false,
15066 * @cfg {Boolean} singleSelect Allow single selection
15068 singleSelect: false,
15071 * @cfg {Boolean} toggleSelect - selecting
15073 toggleSelect : false,
15076 * @cfg {Boolean} tickable - selecting
15081 * Returns the element this view is bound to.
15082 * @return {Roo.Element}
15084 getEl : function(){
15085 return this.wrapEl;
15091 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15093 refresh : function(){
15094 //Roo.log('refresh');
15097 // if we are using something like 'domtemplate', then
15098 // the what gets used is:
15099 // t.applySubtemplate(NAME, data, wrapping data..)
15100 // the outer template then get' applied with
15101 // the store 'extra data'
15102 // and the body get's added to the
15103 // roo-name="data" node?
15104 // <span class='roo-tpl-{name}'></span> ?????
15108 this.clearSelections();
15109 this.el.update("");
15111 var records = this.store.getRange();
15112 if(records.length < 1) {
15114 // is this valid?? = should it render a template??
15116 this.el.update(this.emptyText);
15120 if (this.dataName) {
15121 this.el.update(t.apply(this.store.meta)); //????
15122 el = this.el.child('.roo-tpl-' + this.dataName);
15125 for(var i = 0, len = records.length; i < len; i++){
15126 var data = this.prepareData(records[i].data, i, records[i]);
15127 this.fireEvent("preparedata", this, data, i, records[i]);
15129 var d = Roo.apply({}, data);
15132 Roo.apply(d, {'roo-id' : Roo.id()});
15136 Roo.each(this.parent.item, function(item){
15137 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15140 Roo.apply(d, {'roo-data-checked' : 'checked'});
15144 html[html.length] = Roo.util.Format.trim(
15146 t.applySubtemplate(this.dataName, d, this.store.meta) :
15153 el.update(html.join(""));
15154 this.nodes = el.dom.childNodes;
15155 this.updateIndexes(0);
15160 * Function to override to reformat the data that is sent to
15161 * the template for each node.
15162 * DEPRICATED - use the preparedata event handler.
15163 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15164 * a JSON object for an UpdateManager bound view).
15166 prepareData : function(data, index, record)
15168 this.fireEvent("preparedata", this, data, index, record);
15172 onUpdate : function(ds, record){
15173 // Roo.log('on update');
15174 this.clearSelections();
15175 var index = this.store.indexOf(record);
15176 var n = this.nodes[index];
15177 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15178 n.parentNode.removeChild(n);
15179 this.updateIndexes(index, index);
15185 onAdd : function(ds, records, index)
15187 //Roo.log(['on Add', ds, records, index] );
15188 this.clearSelections();
15189 if(this.nodes.length == 0){
15193 var n = this.nodes[index];
15194 for(var i = 0, len = records.length; i < len; i++){
15195 var d = this.prepareData(records[i].data, i, records[i]);
15197 this.tpl.insertBefore(n, d);
15200 this.tpl.append(this.el, d);
15203 this.updateIndexes(index);
15206 onRemove : function(ds, record, index){
15207 // Roo.log('onRemove');
15208 this.clearSelections();
15209 var el = this.dataName ?
15210 this.el.child('.roo-tpl-' + this.dataName) :
15213 el.dom.removeChild(this.nodes[index]);
15214 this.updateIndexes(index);
15218 * Refresh an individual node.
15219 * @param {Number} index
15221 refreshNode : function(index){
15222 this.onUpdate(this.store, this.store.getAt(index));
15225 updateIndexes : function(startIndex, endIndex){
15226 var ns = this.nodes;
15227 startIndex = startIndex || 0;
15228 endIndex = endIndex || ns.length - 1;
15229 for(var i = startIndex; i <= endIndex; i++){
15230 ns[i].nodeIndex = i;
15235 * Changes the data store this view uses and refresh the view.
15236 * @param {Store} store
15238 setStore : function(store, initial){
15239 if(!initial && this.store){
15240 this.store.un("datachanged", this.refresh);
15241 this.store.un("add", this.onAdd);
15242 this.store.un("remove", this.onRemove);
15243 this.store.un("update", this.onUpdate);
15244 this.store.un("clear", this.refresh);
15245 this.store.un("beforeload", this.onBeforeLoad);
15246 this.store.un("load", this.onLoad);
15247 this.store.un("loadexception", this.onLoad);
15251 store.on("datachanged", this.refresh, this);
15252 store.on("add", this.onAdd, this);
15253 store.on("remove", this.onRemove, this);
15254 store.on("update", this.onUpdate, this);
15255 store.on("clear", this.refresh, this);
15256 store.on("beforeload", this.onBeforeLoad, this);
15257 store.on("load", this.onLoad, this);
15258 store.on("loadexception", this.onLoad, this);
15266 * onbeforeLoad - masks the loading area.
15269 onBeforeLoad : function(store,opts)
15271 //Roo.log('onBeforeLoad');
15273 this.el.update("");
15275 this.el.mask(this.mask ? this.mask : "Loading" );
15277 onLoad : function ()
15284 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15285 * @param {HTMLElement} node
15286 * @return {HTMLElement} The template node
15288 findItemFromChild : function(node){
15289 var el = this.dataName ?
15290 this.el.child('.roo-tpl-' + this.dataName,true) :
15293 if(!node || node.parentNode == el){
15296 var p = node.parentNode;
15297 while(p && p != el){
15298 if(p.parentNode == el){
15307 onClick : function(e){
15308 var item = this.findItemFromChild(e.getTarget());
15310 var index = this.indexOf(item);
15311 if(this.onItemClick(item, index, e) !== false){
15312 this.fireEvent("click", this, index, item, e);
15315 this.clearSelections();
15320 onContextMenu : function(e){
15321 var item = this.findItemFromChild(e.getTarget());
15323 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15328 onDblClick : function(e){
15329 var item = this.findItemFromChild(e.getTarget());
15331 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15335 onItemClick : function(item, index, e)
15337 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15340 if (this.toggleSelect) {
15341 var m = this.isSelected(item) ? 'unselect' : 'select';
15344 _t[m](item, true, false);
15347 if(this.multiSelect || this.singleSelect){
15348 if(this.multiSelect && e.shiftKey && this.lastSelection){
15349 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15351 this.select(item, this.multiSelect && e.ctrlKey);
15352 this.lastSelection = item;
15355 if(!this.tickable){
15356 e.preventDefault();
15364 * Get the number of selected nodes.
15367 getSelectionCount : function(){
15368 return this.selections.length;
15372 * Get the currently selected nodes.
15373 * @return {Array} An array of HTMLElements
15375 getSelectedNodes : function(){
15376 return this.selections;
15380 * Get the indexes of the selected nodes.
15383 getSelectedIndexes : function(){
15384 var indexes = [], s = this.selections;
15385 for(var i = 0, len = s.length; i < len; i++){
15386 indexes.push(s[i].nodeIndex);
15392 * Clear all selections
15393 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15395 clearSelections : function(suppressEvent){
15396 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15397 this.cmp.elements = this.selections;
15398 this.cmp.removeClass(this.selectedClass);
15399 this.selections = [];
15400 if(!suppressEvent){
15401 this.fireEvent("selectionchange", this, this.selections);
15407 * Returns true if the passed node is selected
15408 * @param {HTMLElement/Number} node The node or node index
15409 * @return {Boolean}
15411 isSelected : function(node){
15412 var s = this.selections;
15416 node = this.getNode(node);
15417 return s.indexOf(node) !== -1;
15422 * @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
15423 * @param {Boolean} keepExisting (optional) true to keep existing selections
15424 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15426 select : function(nodeInfo, keepExisting, suppressEvent){
15427 if(nodeInfo instanceof Array){
15429 this.clearSelections(true);
15431 for(var i = 0, len = nodeInfo.length; i < len; i++){
15432 this.select(nodeInfo[i], true, true);
15436 var node = this.getNode(nodeInfo);
15437 if(!node || this.isSelected(node)){
15438 return; // already selected.
15441 this.clearSelections(true);
15444 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15445 Roo.fly(node).addClass(this.selectedClass);
15446 this.selections.push(node);
15447 if(!suppressEvent){
15448 this.fireEvent("selectionchange", this, this.selections);
15456 * @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
15457 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15458 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15460 unselect : function(nodeInfo, keepExisting, suppressEvent)
15462 if(nodeInfo instanceof Array){
15463 Roo.each(this.selections, function(s) {
15464 this.unselect(s, nodeInfo);
15468 var node = this.getNode(nodeInfo);
15469 if(!node || !this.isSelected(node)){
15470 //Roo.log("not selected");
15471 return; // not selected.
15475 Roo.each(this.selections, function(s) {
15477 Roo.fly(node).removeClass(this.selectedClass);
15484 this.selections= ns;
15485 this.fireEvent("selectionchange", this, this.selections);
15489 * Gets a template node.
15490 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15491 * @return {HTMLElement} The node or null if it wasn't found
15493 getNode : function(nodeInfo){
15494 if(typeof nodeInfo == "string"){
15495 return document.getElementById(nodeInfo);
15496 }else if(typeof nodeInfo == "number"){
15497 return this.nodes[nodeInfo];
15503 * Gets a range template nodes.
15504 * @param {Number} startIndex
15505 * @param {Number} endIndex
15506 * @return {Array} An array of nodes
15508 getNodes : function(start, end){
15509 var ns = this.nodes;
15510 start = start || 0;
15511 end = typeof end == "undefined" ? ns.length - 1 : end;
15514 for(var i = start; i <= end; i++){
15518 for(var i = start; i >= end; i--){
15526 * Finds the index of the passed node
15527 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15528 * @return {Number} The index of the node or -1
15530 indexOf : function(node){
15531 node = this.getNode(node);
15532 if(typeof node.nodeIndex == "number"){
15533 return node.nodeIndex;
15535 var ns = this.nodes;
15536 for(var i = 0, len = ns.length; i < len; i++){
15547 * based on jquery fullcalendar
15551 Roo.bootstrap = Roo.bootstrap || {};
15553 * @class Roo.bootstrap.Calendar
15554 * @extends Roo.bootstrap.Component
15555 * Bootstrap Calendar class
15556 * @cfg {Boolean} loadMask (true|false) default false
15557 * @cfg {Object} header generate the user specific header of the calendar, default false
15560 * Create a new Container
15561 * @param {Object} config The config object
15566 Roo.bootstrap.Calendar = function(config){
15567 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15571 * Fires when a date is selected
15572 * @param {DatePicker} this
15573 * @param {Date} date The selected date
15577 * @event monthchange
15578 * Fires when the displayed month changes
15579 * @param {DatePicker} this
15580 * @param {Date} date The selected month
15582 'monthchange': true,
15584 * @event evententer
15585 * Fires when mouse over an event
15586 * @param {Calendar} this
15587 * @param {event} Event
15589 'evententer': true,
15591 * @event eventleave
15592 * Fires when the mouse leaves an
15593 * @param {Calendar} this
15596 'eventleave': true,
15598 * @event eventclick
15599 * Fires when the mouse click an
15600 * @param {Calendar} this
15609 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15612 * @cfg {Number} startDay
15613 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15621 getAutoCreate : function(){
15624 var fc_button = function(name, corner, style, content ) {
15625 return Roo.apply({},{
15627 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15629 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15632 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15643 style : 'width:100%',
15650 cls : 'fc-header-left',
15652 fc_button('prev', 'left', 'arrow', '‹' ),
15653 fc_button('next', 'right', 'arrow', '›' ),
15654 { tag: 'span', cls: 'fc-header-space' },
15655 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15663 cls : 'fc-header-center',
15667 cls: 'fc-header-title',
15670 html : 'month / year'
15678 cls : 'fc-header-right',
15680 /* fc_button('month', 'left', '', 'month' ),
15681 fc_button('week', '', '', 'week' ),
15682 fc_button('day', 'right', '', 'day' )
15694 header = this.header;
15697 var cal_heads = function() {
15699 // fixme - handle this.
15701 for (var i =0; i < Date.dayNames.length; i++) {
15702 var d = Date.dayNames[i];
15705 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15706 html : d.substring(0,3)
15710 ret[0].cls += ' fc-first';
15711 ret[6].cls += ' fc-last';
15714 var cal_cell = function(n) {
15717 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15722 cls: 'fc-day-number',
15726 cls: 'fc-day-content',
15730 style: 'position: relative;' // height: 17px;
15742 var cal_rows = function() {
15745 for (var r = 0; r < 6; r++) {
15752 for (var i =0; i < Date.dayNames.length; i++) {
15753 var d = Date.dayNames[i];
15754 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15757 row.cn[0].cls+=' fc-first';
15758 row.cn[0].cn[0].style = 'min-height:90px';
15759 row.cn[6].cls+=' fc-last';
15763 ret[0].cls += ' fc-first';
15764 ret[4].cls += ' fc-prev-last';
15765 ret[5].cls += ' fc-last';
15772 cls: 'fc-border-separate',
15773 style : 'width:100%',
15781 cls : 'fc-first fc-last',
15799 cls : 'fc-content',
15800 style : "position: relative;",
15803 cls : 'fc-view fc-view-month fc-grid',
15804 style : 'position: relative',
15805 unselectable : 'on',
15808 cls : 'fc-event-container',
15809 style : 'position:absolute;z-index:8;top:0;left:0;'
15827 initEvents : function()
15830 throw "can not find store for calendar";
15836 style: "text-align:center",
15840 style: "background-color:white;width:50%;margin:250 auto",
15844 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15855 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15857 var size = this.el.select('.fc-content', true).first().getSize();
15858 this.maskEl.setSize(size.width, size.height);
15859 this.maskEl.enableDisplayMode("block");
15860 if(!this.loadMask){
15861 this.maskEl.hide();
15864 this.store = Roo.factory(this.store, Roo.data);
15865 this.store.on('load', this.onLoad, this);
15866 this.store.on('beforeload', this.onBeforeLoad, this);
15870 this.cells = this.el.select('.fc-day',true);
15871 //Roo.log(this.cells);
15872 this.textNodes = this.el.query('.fc-day-number');
15873 this.cells.addClassOnOver('fc-state-hover');
15875 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15876 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15877 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15878 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15880 this.on('monthchange', this.onMonthChange, this);
15882 this.update(new Date().clearTime());
15885 resize : function() {
15886 var sz = this.el.getSize();
15888 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15889 this.el.select('.fc-day-content div',true).setHeight(34);
15894 showPrevMonth : function(e){
15895 this.update(this.activeDate.add("mo", -1));
15897 showToday : function(e){
15898 this.update(new Date().clearTime());
15901 showNextMonth : function(e){
15902 this.update(this.activeDate.add("mo", 1));
15906 showPrevYear : function(){
15907 this.update(this.activeDate.add("y", -1));
15911 showNextYear : function(){
15912 this.update(this.activeDate.add("y", 1));
15917 update : function(date)
15919 var vd = this.activeDate;
15920 this.activeDate = date;
15921 // if(vd && this.el){
15922 // var t = date.getTime();
15923 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15924 // Roo.log('using add remove');
15926 // this.fireEvent('monthchange', this, date);
15928 // this.cells.removeClass("fc-state-highlight");
15929 // this.cells.each(function(c){
15930 // if(c.dateValue == t){
15931 // c.addClass("fc-state-highlight");
15932 // setTimeout(function(){
15933 // try{c.dom.firstChild.focus();}catch(e){}
15943 var days = date.getDaysInMonth();
15945 var firstOfMonth = date.getFirstDateOfMonth();
15946 var startingPos = firstOfMonth.getDay()-this.startDay;
15948 if(startingPos < this.startDay){
15952 var pm = date.add(Date.MONTH, -1);
15953 var prevStart = pm.getDaysInMonth()-startingPos;
15955 this.cells = this.el.select('.fc-day',true);
15956 this.textNodes = this.el.query('.fc-day-number');
15957 this.cells.addClassOnOver('fc-state-hover');
15959 var cells = this.cells.elements;
15960 var textEls = this.textNodes;
15962 Roo.each(cells, function(cell){
15963 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15966 days += startingPos;
15968 // convert everything to numbers so it's fast
15969 var day = 86400000;
15970 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15973 //Roo.log(prevStart);
15975 var today = new Date().clearTime().getTime();
15976 var sel = date.clearTime().getTime();
15977 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15978 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15979 var ddMatch = this.disabledDatesRE;
15980 var ddText = this.disabledDatesText;
15981 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15982 var ddaysText = this.disabledDaysText;
15983 var format = this.format;
15985 var setCellClass = function(cal, cell){
15989 //Roo.log('set Cell Class');
15991 var t = d.getTime();
15995 cell.dateValue = t;
15997 cell.className += " fc-today";
15998 cell.className += " fc-state-highlight";
15999 cell.title = cal.todayText;
16002 // disable highlight in other month..
16003 //cell.className += " fc-state-highlight";
16008 cell.className = " fc-state-disabled";
16009 cell.title = cal.minText;
16013 cell.className = " fc-state-disabled";
16014 cell.title = cal.maxText;
16018 if(ddays.indexOf(d.getDay()) != -1){
16019 cell.title = ddaysText;
16020 cell.className = " fc-state-disabled";
16023 if(ddMatch && format){
16024 var fvalue = d.dateFormat(format);
16025 if(ddMatch.test(fvalue)){
16026 cell.title = ddText.replace("%0", fvalue);
16027 cell.className = " fc-state-disabled";
16031 if (!cell.initialClassName) {
16032 cell.initialClassName = cell.dom.className;
16035 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16040 for(; i < startingPos; i++) {
16041 textEls[i].innerHTML = (++prevStart);
16042 d.setDate(d.getDate()+1);
16044 cells[i].className = "fc-past fc-other-month";
16045 setCellClass(this, cells[i]);
16050 for(; i < days; i++){
16051 intDay = i - startingPos + 1;
16052 textEls[i].innerHTML = (intDay);
16053 d.setDate(d.getDate()+1);
16055 cells[i].className = ''; // "x-date-active";
16056 setCellClass(this, cells[i]);
16060 for(; i < 42; i++) {
16061 textEls[i].innerHTML = (++extraDays);
16062 d.setDate(d.getDate()+1);
16064 cells[i].className = "fc-future fc-other-month";
16065 setCellClass(this, cells[i]);
16068 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16070 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16072 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16073 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16075 if(totalRows != 6){
16076 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16077 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16080 this.fireEvent('monthchange', this, date);
16084 if(!this.internalRender){
16085 var main = this.el.dom.firstChild;
16086 var w = main.offsetWidth;
16087 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16088 Roo.fly(main).setWidth(w);
16089 this.internalRender = true;
16090 // opera does not respect the auto grow header center column
16091 // then, after it gets a width opera refuses to recalculate
16092 // without a second pass
16093 if(Roo.isOpera && !this.secondPass){
16094 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16095 this.secondPass = true;
16096 this.update.defer(10, this, [date]);
16103 findCell : function(dt) {
16104 dt = dt.clearTime().getTime();
16106 this.cells.each(function(c){
16107 //Roo.log("check " +c.dateValue + '?=' + dt);
16108 if(c.dateValue == dt){
16118 findCells : function(ev) {
16119 var s = ev.start.clone().clearTime().getTime();
16121 var e= ev.end.clone().clearTime().getTime();
16124 this.cells.each(function(c){
16125 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16127 if(c.dateValue > e){
16130 if(c.dateValue < s){
16139 // findBestRow: function(cells)
16143 // for (var i =0 ; i < cells.length;i++) {
16144 // ret = Math.max(cells[i].rows || 0,ret);
16151 addItem : function(ev)
16153 // look for vertical location slot in
16154 var cells = this.findCells(ev);
16156 // ev.row = this.findBestRow(cells);
16158 // work out the location.
16162 for(var i =0; i < cells.length; i++) {
16164 cells[i].row = cells[0].row;
16167 cells[i].row = cells[i].row + 1;
16177 if (crow.start.getY() == cells[i].getY()) {
16179 crow.end = cells[i];
16196 cells[0].events.push(ev);
16198 this.calevents.push(ev);
16201 clearEvents: function() {
16203 if(!this.calevents){
16207 Roo.each(this.cells.elements, function(c){
16213 Roo.each(this.calevents, function(e) {
16214 Roo.each(e.els, function(el) {
16215 el.un('mouseenter' ,this.onEventEnter, this);
16216 el.un('mouseleave' ,this.onEventLeave, this);
16221 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16227 renderEvents: function()
16231 this.cells.each(function(c) {
16240 if(c.row != c.events.length){
16241 r = 4 - (4 - (c.row - c.events.length));
16244 c.events = ev.slice(0, r);
16245 c.more = ev.slice(r);
16247 if(c.more.length && c.more.length == 1){
16248 c.events.push(c.more.pop());
16251 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16255 this.cells.each(function(c) {
16257 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16260 for (var e = 0; e < c.events.length; e++){
16261 var ev = c.events[e];
16262 var rows = ev.rows;
16264 for(var i = 0; i < rows.length; i++) {
16266 // how many rows should it span..
16269 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16270 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16272 unselectable : "on",
16275 cls: 'fc-event-inner',
16279 // cls: 'fc-event-time',
16280 // html : cells.length > 1 ? '' : ev.time
16284 cls: 'fc-event-title',
16285 html : String.format('{0}', ev.title)
16292 cls: 'ui-resizable-handle ui-resizable-e',
16293 html : '  '
16300 cfg.cls += ' fc-event-start';
16302 if ((i+1) == rows.length) {
16303 cfg.cls += ' fc-event-end';
16306 var ctr = _this.el.select('.fc-event-container',true).first();
16307 var cg = ctr.createChild(cfg);
16309 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16310 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16312 var r = (c.more.length) ? 1 : 0;
16313 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16314 cg.setWidth(ebox.right - sbox.x -2);
16316 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16317 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16318 cg.on('click', _this.onEventClick, _this, ev);
16329 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16330 style : 'position: absolute',
16331 unselectable : "on",
16334 cls: 'fc-event-inner',
16338 cls: 'fc-event-title',
16346 cls: 'ui-resizable-handle ui-resizable-e',
16347 html : '  '
16353 var ctr = _this.el.select('.fc-event-container',true).first();
16354 var cg = ctr.createChild(cfg);
16356 var sbox = c.select('.fc-day-content',true).first().getBox();
16357 var ebox = c.select('.fc-day-content',true).first().getBox();
16359 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16360 cg.setWidth(ebox.right - sbox.x -2);
16362 cg.on('click', _this.onMoreEventClick, _this, c.more);
16372 onEventEnter: function (e, el,event,d) {
16373 this.fireEvent('evententer', this, el, event);
16376 onEventLeave: function (e, el,event,d) {
16377 this.fireEvent('eventleave', this, el, event);
16380 onEventClick: function (e, el,event,d) {
16381 this.fireEvent('eventclick', this, el, event);
16384 onMonthChange: function () {
16388 onMoreEventClick: function(e, el, more)
16392 this.calpopover.placement = 'right';
16393 this.calpopover.setTitle('More');
16395 this.calpopover.setContent('');
16397 var ctr = this.calpopover.el.select('.popover-content', true).first();
16399 Roo.each(more, function(m){
16401 cls : 'fc-event-hori fc-event-draggable',
16404 var cg = ctr.createChild(cfg);
16406 cg.on('click', _this.onEventClick, _this, m);
16409 this.calpopover.show(el);
16414 onLoad: function ()
16416 this.calevents = [];
16419 if(this.store.getCount() > 0){
16420 this.store.data.each(function(d){
16423 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16424 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16425 time : d.data.start_time,
16426 title : d.data.title,
16427 description : d.data.description,
16428 venue : d.data.venue
16433 this.renderEvents();
16435 if(this.calevents.length && this.loadMask){
16436 this.maskEl.hide();
16440 onBeforeLoad: function()
16442 this.clearEvents();
16444 this.maskEl.show();
16458 * @class Roo.bootstrap.Popover
16459 * @extends Roo.bootstrap.Component
16460 * Bootstrap Popover class
16461 * @cfg {String} html contents of the popover (or false to use children..)
16462 * @cfg {String} title of popover (or false to hide)
16463 * @cfg {String} placement how it is placed
16464 * @cfg {String} trigger click || hover (or false to trigger manually)
16465 * @cfg {String} over what (parent or false to trigger manually.)
16466 * @cfg {Number} delay - delay before showing
16469 * Create a new Popover
16470 * @param {Object} config The config object
16473 Roo.bootstrap.Popover = function(config){
16474 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16480 * After the popover show
16482 * @param {Roo.bootstrap.Popover} this
16487 * After the popover hide
16489 * @param {Roo.bootstrap.Popover} this
16495 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16497 title: 'Fill in a title',
16500 placement : 'right',
16501 trigger : 'hover', // hover
16507 can_build_overlaid : false,
16509 getChildContainer : function()
16511 return this.el.select('.popover-content',true).first();
16514 getAutoCreate : function(){
16517 cls : 'popover roo-dynamic',
16518 style: 'display:block',
16524 cls : 'popover-inner',
16528 cls: 'popover-title',
16532 cls : 'popover-content',
16543 setTitle: function(str)
16546 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16548 setContent: function(str)
16551 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16553 // as it get's added to the bottom of the page.
16554 onRender : function(ct, position)
16556 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16558 var cfg = Roo.apply({}, this.getAutoCreate());
16562 cfg.cls += ' ' + this.cls;
16565 cfg.style = this.style;
16567 //Roo.log("adding to ");
16568 this.el = Roo.get(document.body).createChild(cfg, position);
16569 // Roo.log(this.el);
16574 initEvents : function()
16576 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16577 this.el.enableDisplayMode('block');
16579 if (this.over === false) {
16582 if (this.triggers === false) {
16585 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16586 var triggers = this.trigger ? this.trigger.split(' ') : [];
16587 Roo.each(triggers, function(trigger) {
16589 if (trigger == 'click') {
16590 on_el.on('click', this.toggle, this);
16591 } else if (trigger != 'manual') {
16592 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16593 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16595 on_el.on(eventIn ,this.enter, this);
16596 on_el.on(eventOut, this.leave, this);
16607 toggle : function () {
16608 this.hoverState == 'in' ? this.leave() : this.enter();
16611 enter : function () {
16613 clearTimeout(this.timeout);
16615 this.hoverState = 'in';
16617 if (!this.delay || !this.delay.show) {
16622 this.timeout = setTimeout(function () {
16623 if (_t.hoverState == 'in') {
16626 }, this.delay.show)
16629 leave : function() {
16630 clearTimeout(this.timeout);
16632 this.hoverState = 'out';
16634 if (!this.delay || !this.delay.hide) {
16639 this.timeout = setTimeout(function () {
16640 if (_t.hoverState == 'out') {
16643 }, this.delay.hide)
16646 show : function (on_el)
16649 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16653 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16654 if (this.html !== false) {
16655 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16657 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16658 if (!this.title.length) {
16659 this.el.select('.popover-title',true).hide();
16662 var placement = typeof this.placement == 'function' ?
16663 this.placement.call(this, this.el, on_el) :
16666 var autoToken = /\s?auto?\s?/i;
16667 var autoPlace = autoToken.test(placement);
16669 placement = placement.replace(autoToken, '') || 'top';
16673 //this.el.setXY([0,0]);
16675 this.el.dom.style.display='block';
16676 this.el.addClass(placement);
16678 //this.el.appendTo(on_el);
16680 var p = this.getPosition();
16681 var box = this.el.getBox();
16686 var align = Roo.bootstrap.Popover.alignment[placement];
16687 this.el.alignTo(on_el, align[0],align[1]);
16688 //var arrow = this.el.select('.arrow',true).first();
16689 //arrow.set(align[2],
16691 this.el.addClass('in');
16694 if (this.el.hasClass('fade')) {
16698 this.hoverState = 'in';
16700 this.fireEvent('show', this);
16705 this.el.setXY([0,0]);
16706 this.el.removeClass('in');
16708 this.hoverState = null;
16710 this.fireEvent('hide', this);
16715 Roo.bootstrap.Popover.alignment = {
16716 'left' : ['r-l', [-10,0], 'right'],
16717 'right' : ['l-r', [10,0], 'left'],
16718 'bottom' : ['t-b', [0,10], 'top'],
16719 'top' : [ 'b-t', [0,-10], 'bottom']
16730 * @class Roo.bootstrap.Progress
16731 * @extends Roo.bootstrap.Component
16732 * Bootstrap Progress class
16733 * @cfg {Boolean} striped striped of the progress bar
16734 * @cfg {Boolean} active animated of the progress bar
16738 * Create a new Progress
16739 * @param {Object} config The config object
16742 Roo.bootstrap.Progress = function(config){
16743 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16746 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16751 getAutoCreate : function(){
16759 cfg.cls += ' progress-striped';
16763 cfg.cls += ' active';
16782 * @class Roo.bootstrap.ProgressBar
16783 * @extends Roo.bootstrap.Component
16784 * Bootstrap ProgressBar class
16785 * @cfg {Number} aria_valuenow aria-value now
16786 * @cfg {Number} aria_valuemin aria-value min
16787 * @cfg {Number} aria_valuemax aria-value max
16788 * @cfg {String} label label for the progress bar
16789 * @cfg {String} panel (success | info | warning | danger )
16790 * @cfg {String} role role of the progress bar
16791 * @cfg {String} sr_only text
16795 * Create a new ProgressBar
16796 * @param {Object} config The config object
16799 Roo.bootstrap.ProgressBar = function(config){
16800 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16803 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16807 aria_valuemax : 100,
16813 getAutoCreate : function()
16818 cls: 'progress-bar',
16819 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16831 cfg.role = this.role;
16834 if(this.aria_valuenow){
16835 cfg['aria-valuenow'] = this.aria_valuenow;
16838 if(this.aria_valuemin){
16839 cfg['aria-valuemin'] = this.aria_valuemin;
16842 if(this.aria_valuemax){
16843 cfg['aria-valuemax'] = this.aria_valuemax;
16846 if(this.label && !this.sr_only){
16847 cfg.html = this.label;
16851 cfg.cls += ' progress-bar-' + this.panel;
16857 update : function(aria_valuenow)
16859 this.aria_valuenow = aria_valuenow;
16861 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16876 * @class Roo.bootstrap.TabGroup
16877 * @extends Roo.bootstrap.Column
16878 * Bootstrap Column class
16879 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16880 * @cfg {Boolean} carousel true to make the group behave like a carousel
16881 * @cfg {Boolean} bullets show bullets for the panels
16882 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16883 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16884 * @cfg {Boolean} showarrow (true|false) show arrow default true
16887 * Create a new TabGroup
16888 * @param {Object} config The config object
16891 Roo.bootstrap.TabGroup = function(config){
16892 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16894 this.navId = Roo.id();
16897 Roo.bootstrap.TabGroup.register(this);
16901 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16904 transition : false,
16909 slideOnTouch : false,
16912 getAutoCreate : function()
16914 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16916 cfg.cls += ' tab-content';
16918 if (this.carousel) {
16919 cfg.cls += ' carousel slide';
16922 cls : 'carousel-inner',
16926 if(this.bullets && !Roo.isTouch){
16929 cls : 'carousel-bullets',
16933 if(this.bullets_cls){
16934 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16941 cfg.cn[0].cn.push(bullets);
16944 if(this.showarrow){
16945 cfg.cn[0].cn.push({
16947 class : 'carousel-arrow',
16951 class : 'carousel-prev',
16955 class : 'fa fa-chevron-left'
16961 class : 'carousel-next',
16965 class : 'fa fa-chevron-right'
16978 initEvents: function()
16980 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16981 // this.el.on("touchstart", this.onTouchStart, this);
16984 if(this.autoslide){
16987 this.slideFn = window.setInterval(function() {
16988 _this.showPanelNext();
16992 if(this.showarrow){
16993 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16994 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17000 // onTouchStart : function(e, el, o)
17002 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17006 // this.showPanelNext();
17010 getChildContainer : function()
17012 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17016 * register a Navigation item
17017 * @param {Roo.bootstrap.NavItem} the navitem to add
17019 register : function(item)
17021 this.tabs.push( item);
17022 item.navId = this.navId; // not really needed..
17027 getActivePanel : function()
17030 Roo.each(this.tabs, function(t) {
17040 getPanelByName : function(n)
17043 Roo.each(this.tabs, function(t) {
17044 if (t.tabId == n) {
17052 indexOfPanel : function(p)
17055 Roo.each(this.tabs, function(t,i) {
17056 if (t.tabId == p.tabId) {
17065 * show a specific panel
17066 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17067 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17069 showPanel : function (pan)
17071 if(this.transition || typeof(pan) == 'undefined'){
17072 Roo.log("waiting for the transitionend");
17076 if (typeof(pan) == 'number') {
17077 pan = this.tabs[pan];
17080 if (typeof(pan) == 'string') {
17081 pan = this.getPanelByName(pan);
17084 var cur = this.getActivePanel();
17087 Roo.log('pan or acitve pan is undefined');
17091 if (pan.tabId == this.getActivePanel().tabId) {
17095 if (false === cur.fireEvent('beforedeactivate')) {
17099 if(this.bullets > 0 && !Roo.isTouch){
17100 this.setActiveBullet(this.indexOfPanel(pan));
17103 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17105 this.transition = true;
17106 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17107 var lr = dir == 'next' ? 'left' : 'right';
17108 pan.el.addClass(dir); // or prev
17109 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17110 cur.el.addClass(lr); // or right
17111 pan.el.addClass(lr);
17114 cur.el.on('transitionend', function() {
17115 Roo.log("trans end?");
17117 pan.el.removeClass([lr,dir]);
17118 pan.setActive(true);
17120 cur.el.removeClass([lr]);
17121 cur.setActive(false);
17123 _this.transition = false;
17125 }, this, { single: true } );
17130 cur.setActive(false);
17131 pan.setActive(true);
17136 showPanelNext : function()
17138 var i = this.indexOfPanel(this.getActivePanel());
17140 if (i >= this.tabs.length - 1 && !this.autoslide) {
17144 if (i >= this.tabs.length - 1 && this.autoslide) {
17148 this.showPanel(this.tabs[i+1]);
17151 showPanelPrev : function()
17153 var i = this.indexOfPanel(this.getActivePanel());
17155 if (i < 1 && !this.autoslide) {
17159 if (i < 1 && this.autoslide) {
17160 i = this.tabs.length;
17163 this.showPanel(this.tabs[i-1]);
17167 addBullet: function()
17169 if(!this.bullets || Roo.isTouch){
17172 var ctr = this.el.select('.carousel-bullets',true).first();
17173 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17174 var bullet = ctr.createChild({
17175 cls : 'bullet bullet-' + i
17176 },ctr.dom.lastChild);
17181 bullet.on('click', (function(e, el, o, ii, t){
17183 e.preventDefault();
17185 this.showPanel(ii);
17187 if(this.autoslide && this.slideFn){
17188 clearInterval(this.slideFn);
17189 this.slideFn = window.setInterval(function() {
17190 _this.showPanelNext();
17194 }).createDelegate(this, [i, bullet], true));
17199 setActiveBullet : function(i)
17205 Roo.each(this.el.select('.bullet', true).elements, function(el){
17206 el.removeClass('selected');
17209 var bullet = this.el.select('.bullet-' + i, true).first();
17215 bullet.addClass('selected');
17226 Roo.apply(Roo.bootstrap.TabGroup, {
17230 * register a Navigation Group
17231 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17233 register : function(navgrp)
17235 this.groups[navgrp.navId] = navgrp;
17239 * fetch a Navigation Group based on the navigation ID
17240 * if one does not exist , it will get created.
17241 * @param {string} the navgroup to add
17242 * @returns {Roo.bootstrap.NavGroup} the navgroup
17244 get: function(navId) {
17245 if (typeof(this.groups[navId]) == 'undefined') {
17246 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17248 return this.groups[navId] ;
17263 * @class Roo.bootstrap.TabPanel
17264 * @extends Roo.bootstrap.Component
17265 * Bootstrap TabPanel class
17266 * @cfg {Boolean} active panel active
17267 * @cfg {String} html panel content
17268 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17269 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17270 * @cfg {String} href click to link..
17274 * Create a new TabPanel
17275 * @param {Object} config The config object
17278 Roo.bootstrap.TabPanel = function(config){
17279 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17283 * Fires when the active status changes
17284 * @param {Roo.bootstrap.TabPanel} this
17285 * @param {Boolean} state the new state
17290 * @event beforedeactivate
17291 * Fires before a tab is de-activated - can be used to do validation on a form.
17292 * @param {Roo.bootstrap.TabPanel} this
17293 * @return {Boolean} false if there is an error
17296 'beforedeactivate': true
17299 this.tabId = this.tabId || Roo.id();
17303 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17311 getAutoCreate : function(){
17314 // item is needed for carousel - not sure if it has any effect otherwise
17315 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17316 html: this.html || ''
17320 cfg.cls += ' active';
17324 cfg.tabId = this.tabId;
17331 initEvents: function()
17333 var p = this.parent();
17335 this.navId = this.navId || p.navId;
17337 if (typeof(this.navId) != 'undefined') {
17338 // not really needed.. but just in case.. parent should be a NavGroup.
17339 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17343 var i = tg.tabs.length - 1;
17345 if(this.active && tg.bullets > 0 && i < tg.bullets){
17346 tg.setActiveBullet(i);
17350 this.el.on('click', this.onClick, this);
17353 this.el.on("touchstart", this.onTouchStart, this);
17354 this.el.on("touchmove", this.onTouchMove, this);
17355 this.el.on("touchend", this.onTouchEnd, this);
17360 onRender : function(ct, position)
17362 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17365 setActive : function(state)
17367 Roo.log("panel - set active " + this.tabId + "=" + state);
17369 this.active = state;
17371 this.el.removeClass('active');
17373 } else if (!this.el.hasClass('active')) {
17374 this.el.addClass('active');
17377 this.fireEvent('changed', this, state);
17380 onClick : function(e)
17382 e.preventDefault();
17384 if(!this.href.length){
17388 window.location.href = this.href;
17397 onTouchStart : function(e)
17399 this.swiping = false;
17401 this.startX = e.browserEvent.touches[0].clientX;
17402 this.startY = e.browserEvent.touches[0].clientY;
17405 onTouchMove : function(e)
17407 this.swiping = true;
17409 this.endX = e.browserEvent.touches[0].clientX;
17410 this.endY = e.browserEvent.touches[0].clientY;
17413 onTouchEnd : function(e)
17420 var tabGroup = this.parent();
17422 if(this.endX > this.startX){ // swiping right
17423 tabGroup.showPanelPrev();
17427 if(this.startX > this.endX){ // swiping left
17428 tabGroup.showPanelNext();
17447 * @class Roo.bootstrap.DateField
17448 * @extends Roo.bootstrap.Input
17449 * Bootstrap DateField class
17450 * @cfg {Number} weekStart default 0
17451 * @cfg {String} viewMode default empty, (months|years)
17452 * @cfg {String} minViewMode default empty, (months|years)
17453 * @cfg {Number} startDate default -Infinity
17454 * @cfg {Number} endDate default Infinity
17455 * @cfg {Boolean} todayHighlight default false
17456 * @cfg {Boolean} todayBtn default false
17457 * @cfg {Boolean} calendarWeeks default false
17458 * @cfg {Object} daysOfWeekDisabled default empty
17459 * @cfg {Boolean} singleMode default false (true | false)
17461 * @cfg {Boolean} keyboardNavigation default true
17462 * @cfg {String} language default en
17465 * Create a new DateField
17466 * @param {Object} config The config object
17469 Roo.bootstrap.DateField = function(config){
17470 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17474 * Fires when this field show.
17475 * @param {Roo.bootstrap.DateField} this
17476 * @param {Mixed} date The date value
17481 * Fires when this field hide.
17482 * @param {Roo.bootstrap.DateField} this
17483 * @param {Mixed} date The date value
17488 * Fires when select a date.
17489 * @param {Roo.bootstrap.DateField} this
17490 * @param {Mixed} date The date value
17494 * @event beforeselect
17495 * Fires when before select a date.
17496 * @param {Roo.bootstrap.DateField} this
17497 * @param {Mixed} date The date value
17499 beforeselect : true
17503 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
17506 * @cfg {String} format
17507 * The default date format string which can be overriden for localization support. The format must be
17508 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17512 * @cfg {String} altFormats
17513 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17514 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17516 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17524 todayHighlight : false,
17530 keyboardNavigation: true,
17532 calendarWeeks: false,
17534 startDate: -Infinity,
17538 daysOfWeekDisabled: [],
17542 singleMode : false,
17544 UTCDate: function()
17546 return new Date(Date.UTC.apply(Date, arguments));
17549 UTCToday: function()
17551 var today = new Date();
17552 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17555 getDate: function() {
17556 var d = this.getUTCDate();
17557 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17560 getUTCDate: function() {
17564 setDate: function(d) {
17565 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17568 setUTCDate: function(d) {
17570 this.setValue(this.formatDate(this.date));
17573 onRender: function(ct, position)
17576 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17578 this.language = this.language || 'en';
17579 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17580 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17582 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17583 this.format = this.format || 'm/d/y';
17584 this.isInline = false;
17585 this.isInput = true;
17586 this.component = this.el.select('.add-on', true).first() || false;
17587 this.component = (this.component && this.component.length === 0) ? false : this.component;
17588 this.hasInput = this.component && this.inputEl().length;
17590 if (typeof(this.minViewMode === 'string')) {
17591 switch (this.minViewMode) {
17593 this.minViewMode = 1;
17596 this.minViewMode = 2;
17599 this.minViewMode = 0;
17604 if (typeof(this.viewMode === 'string')) {
17605 switch (this.viewMode) {
17618 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17620 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17622 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17624 this.picker().on('mousedown', this.onMousedown, this);
17625 this.picker().on('click', this.onClick, this);
17627 this.picker().addClass('datepicker-dropdown');
17629 this.startViewMode = this.viewMode;
17631 if(this.singleMode){
17632 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17633 v.setVisibilityMode(Roo.Element.DISPLAY);
17637 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17638 v.setStyle('width', '189px');
17642 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17643 if(!this.calendarWeeks){
17648 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17649 v.attr('colspan', function(i, val){
17650 return parseInt(val) + 1;
17655 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17657 this.setStartDate(this.startDate);
17658 this.setEndDate(this.endDate);
17660 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17667 if(this.isInline) {
17672 picker : function()
17674 return this.pickerEl;
17675 // return this.el.select('.datepicker', true).first();
17678 fillDow: function()
17680 var dowCnt = this.weekStart;
17689 if(this.calendarWeeks){
17697 while (dowCnt < this.weekStart + 7) {
17701 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17705 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17708 fillMonths: function()
17711 var months = this.picker().select('>.datepicker-months td', true).first();
17713 months.dom.innerHTML = '';
17719 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17722 months.createChild(month);
17729 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;
17731 if (this.date < this.startDate) {
17732 this.viewDate = new Date(this.startDate);
17733 } else if (this.date > this.endDate) {
17734 this.viewDate = new Date(this.endDate);
17736 this.viewDate = new Date(this.date);
17744 var d = new Date(this.viewDate),
17745 year = d.getUTCFullYear(),
17746 month = d.getUTCMonth(),
17747 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17748 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17749 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17750 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17751 currentDate = this.date && this.date.valueOf(),
17752 today = this.UTCToday();
17754 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17756 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17758 // this.picker.select('>tfoot th.today').
17759 // .text(dates[this.language].today)
17760 // .toggle(this.todayBtn !== false);
17762 this.updateNavArrows();
17765 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17767 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17769 prevMonth.setUTCDate(day);
17771 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17773 var nextMonth = new Date(prevMonth);
17775 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17777 nextMonth = nextMonth.valueOf();
17779 var fillMonths = false;
17781 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17783 while(prevMonth.valueOf() < nextMonth) {
17786 if (prevMonth.getUTCDay() === this.weekStart) {
17788 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17796 if(this.calendarWeeks){
17797 // ISO 8601: First week contains first thursday.
17798 // ISO also states week starts on Monday, but we can be more abstract here.
17800 // Start of current week: based on weekstart/current date
17801 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17802 // Thursday of this week
17803 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17804 // First Thursday of year, year from thursday
17805 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17806 // Calendar week: ms between thursdays, div ms per day, div 7 days
17807 calWeek = (th - yth) / 864e5 / 7 + 1;
17809 fillMonths.cn.push({
17817 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17819 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17822 if (this.todayHighlight &&
17823 prevMonth.getUTCFullYear() == today.getFullYear() &&
17824 prevMonth.getUTCMonth() == today.getMonth() &&
17825 prevMonth.getUTCDate() == today.getDate()) {
17826 clsName += ' today';
17829 if (currentDate && prevMonth.valueOf() === currentDate) {
17830 clsName += ' active';
17833 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17834 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17835 clsName += ' disabled';
17838 fillMonths.cn.push({
17840 cls: 'day ' + clsName,
17841 html: prevMonth.getDate()
17844 prevMonth.setDate(prevMonth.getDate()+1);
17847 var currentYear = this.date && this.date.getUTCFullYear();
17848 var currentMonth = this.date && this.date.getUTCMonth();
17850 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17852 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17853 v.removeClass('active');
17855 if(currentYear === year && k === currentMonth){
17856 v.addClass('active');
17859 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17860 v.addClass('disabled');
17866 year = parseInt(year/10, 10) * 10;
17868 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17870 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17873 for (var i = -1; i < 11; i++) {
17874 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17876 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17884 showMode: function(dir)
17887 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17890 Roo.each(this.picker().select('>div',true).elements, function(v){
17891 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17894 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17899 if(this.isInline) {
17903 this.picker().removeClass(['bottom', 'top']);
17905 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17907 * place to the top of element!
17911 this.picker().addClass('top');
17912 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17917 this.picker().addClass('bottom');
17919 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17922 parseDate : function(value)
17924 if(!value || value instanceof Date){
17927 var v = Date.parseDate(value, this.format);
17928 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17929 v = Date.parseDate(value, 'Y-m-d');
17931 if(!v && this.altFormats){
17932 if(!this.altFormatsArray){
17933 this.altFormatsArray = this.altFormats.split("|");
17935 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17936 v = Date.parseDate(value, this.altFormatsArray[i]);
17942 formatDate : function(date, fmt)
17944 return (!date || !(date instanceof Date)) ?
17945 date : date.dateFormat(fmt || this.format);
17948 onFocus : function()
17950 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17954 onBlur : function()
17956 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17958 var d = this.inputEl().getValue();
17967 this.picker().show();
17971 this.fireEvent('show', this, this.date);
17976 if(this.isInline) {
17979 this.picker().hide();
17980 this.viewMode = this.startViewMode;
17983 this.fireEvent('hide', this, this.date);
17987 onMousedown: function(e)
17989 e.stopPropagation();
17990 e.preventDefault();
17995 Roo.bootstrap.DateField.superclass.keyup.call(this);
17999 setValue: function(v)
18001 if(this.fireEvent('beforeselect', this, v) !== false){
18002 var d = new Date(this.parseDate(v) ).clearTime();
18004 if(isNaN(d.getTime())){
18005 this.date = this.viewDate = '';
18006 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18010 v = this.formatDate(d);
18012 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18014 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18018 this.fireEvent('select', this, this.date);
18022 getValue: function()
18024 return this.formatDate(this.date);
18027 fireKey: function(e)
18029 if (!this.picker().isVisible()){
18030 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18036 var dateChanged = false,
18038 newDate, newViewDate;
18043 e.preventDefault();
18047 if (!this.keyboardNavigation) {
18050 dir = e.keyCode == 37 ? -1 : 1;
18053 newDate = this.moveYear(this.date, dir);
18054 newViewDate = this.moveYear(this.viewDate, dir);
18055 } else if (e.shiftKey){
18056 newDate = this.moveMonth(this.date, dir);
18057 newViewDate = this.moveMonth(this.viewDate, dir);
18059 newDate = new Date(this.date);
18060 newDate.setUTCDate(this.date.getUTCDate() + dir);
18061 newViewDate = new Date(this.viewDate);
18062 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18064 if (this.dateWithinRange(newDate)){
18065 this.date = newDate;
18066 this.viewDate = newViewDate;
18067 this.setValue(this.formatDate(this.date));
18069 e.preventDefault();
18070 dateChanged = true;
18075 if (!this.keyboardNavigation) {
18078 dir = e.keyCode == 38 ? -1 : 1;
18080 newDate = this.moveYear(this.date, dir);
18081 newViewDate = this.moveYear(this.viewDate, dir);
18082 } else if (e.shiftKey){
18083 newDate = this.moveMonth(this.date, dir);
18084 newViewDate = this.moveMonth(this.viewDate, dir);
18086 newDate = new Date(this.date);
18087 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18088 newViewDate = new Date(this.viewDate);
18089 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18091 if (this.dateWithinRange(newDate)){
18092 this.date = newDate;
18093 this.viewDate = newViewDate;
18094 this.setValue(this.formatDate(this.date));
18096 e.preventDefault();
18097 dateChanged = true;
18101 this.setValue(this.formatDate(this.date));
18103 e.preventDefault();
18106 this.setValue(this.formatDate(this.date));
18120 onClick: function(e)
18122 e.stopPropagation();
18123 e.preventDefault();
18125 var target = e.getTarget();
18127 if(target.nodeName.toLowerCase() === 'i'){
18128 target = Roo.get(target).dom.parentNode;
18131 var nodeName = target.nodeName;
18132 var className = target.className;
18133 var html = target.innerHTML;
18134 //Roo.log(nodeName);
18136 switch(nodeName.toLowerCase()) {
18138 switch(className) {
18144 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18145 switch(this.viewMode){
18147 this.viewDate = this.moveMonth(this.viewDate, dir);
18151 this.viewDate = this.moveYear(this.viewDate, dir);
18157 var date = new Date();
18158 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18160 this.setValue(this.formatDate(this.date));
18167 if (className.indexOf('disabled') < 0) {
18168 this.viewDate.setUTCDate(1);
18169 if (className.indexOf('month') > -1) {
18170 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18172 var year = parseInt(html, 10) || 0;
18173 this.viewDate.setUTCFullYear(year);
18177 if(this.singleMode){
18178 this.setValue(this.formatDate(this.viewDate));
18189 //Roo.log(className);
18190 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18191 var day = parseInt(html, 10) || 1;
18192 var year = this.viewDate.getUTCFullYear(),
18193 month = this.viewDate.getUTCMonth();
18195 if (className.indexOf('old') > -1) {
18202 } else if (className.indexOf('new') > -1) {
18210 //Roo.log([year,month,day]);
18211 this.date = this.UTCDate(year, month, day,0,0,0,0);
18212 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18214 //Roo.log(this.formatDate(this.date));
18215 this.setValue(this.formatDate(this.date));
18222 setStartDate: function(startDate)
18224 this.startDate = startDate || -Infinity;
18225 if (this.startDate !== -Infinity) {
18226 this.startDate = this.parseDate(this.startDate);
18229 this.updateNavArrows();
18232 setEndDate: function(endDate)
18234 this.endDate = endDate || Infinity;
18235 if (this.endDate !== Infinity) {
18236 this.endDate = this.parseDate(this.endDate);
18239 this.updateNavArrows();
18242 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18244 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18245 if (typeof(this.daysOfWeekDisabled) !== 'object') {
18246 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18248 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18249 return parseInt(d, 10);
18252 this.updateNavArrows();
18255 updateNavArrows: function()
18257 if(this.singleMode){
18261 var d = new Date(this.viewDate),
18262 year = d.getUTCFullYear(),
18263 month = d.getUTCMonth();
18265 Roo.each(this.picker().select('.prev', true).elements, function(v){
18267 switch (this.viewMode) {
18270 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18276 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18283 Roo.each(this.picker().select('.next', true).elements, function(v){
18285 switch (this.viewMode) {
18288 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18294 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18302 moveMonth: function(date, dir)
18307 var new_date = new Date(date.valueOf()),
18308 day = new_date.getUTCDate(),
18309 month = new_date.getUTCMonth(),
18310 mag = Math.abs(dir),
18312 dir = dir > 0 ? 1 : -1;
18315 // If going back one month, make sure month is not current month
18316 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18318 return new_date.getUTCMonth() == month;
18320 // If going forward one month, make sure month is as expected
18321 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18323 return new_date.getUTCMonth() != new_month;
18325 new_month = month + dir;
18326 new_date.setUTCMonth(new_month);
18327 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18328 if (new_month < 0 || new_month > 11) {
18329 new_month = (new_month + 12) % 12;
18332 // For magnitudes >1, move one month at a time...
18333 for (var i=0; i<mag; i++) {
18334 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18335 new_date = this.moveMonth(new_date, dir);
18337 // ...then reset the day, keeping it in the new month
18338 new_month = new_date.getUTCMonth();
18339 new_date.setUTCDate(day);
18341 return new_month != new_date.getUTCMonth();
18344 // Common date-resetting loop -- if date is beyond end of month, make it
18347 new_date.setUTCDate(--day);
18348 new_date.setUTCMonth(new_month);
18353 moveYear: function(date, dir)
18355 return this.moveMonth(date, dir*12);
18358 dateWithinRange: function(date)
18360 return date >= this.startDate && date <= this.endDate;
18366 this.picker().remove();
18369 validateValue : function(value)
18371 if(value.length < 1) {
18372 if(this.allowBlank){
18378 if(value.length < this.minLength){
18381 if(value.length > this.maxLength){
18385 var vt = Roo.form.VTypes;
18386 if(!vt[this.vtype](value, this)){
18390 if(typeof this.validator == "function"){
18391 var msg = this.validator(value);
18397 if(this.regex && !this.regex.test(value)){
18401 if(typeof(this.parseDate(value)) == 'undefined'){
18405 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18409 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18419 Roo.apply(Roo.bootstrap.DateField, {
18430 html: '<i class="fa fa-arrow-left"/>'
18440 html: '<i class="fa fa-arrow-right"/>'
18482 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18483 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18484 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18485 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18486 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18499 navFnc: 'FullYear',
18504 navFnc: 'FullYear',
18509 Roo.apply(Roo.bootstrap.DateField, {
18513 cls: 'datepicker dropdown-menu roo-dynamic',
18517 cls: 'datepicker-days',
18521 cls: 'table-condensed',
18523 Roo.bootstrap.DateField.head,
18527 Roo.bootstrap.DateField.footer
18534 cls: 'datepicker-months',
18538 cls: 'table-condensed',
18540 Roo.bootstrap.DateField.head,
18541 Roo.bootstrap.DateField.content,
18542 Roo.bootstrap.DateField.footer
18549 cls: 'datepicker-years',
18553 cls: 'table-condensed',
18555 Roo.bootstrap.DateField.head,
18556 Roo.bootstrap.DateField.content,
18557 Roo.bootstrap.DateField.footer
18576 * @class Roo.bootstrap.TimeField
18577 * @extends Roo.bootstrap.Input
18578 * Bootstrap DateField class
18582 * Create a new TimeField
18583 * @param {Object} config The config object
18586 Roo.bootstrap.TimeField = function(config){
18587 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18591 * Fires when this field show.
18592 * @param {Roo.bootstrap.DateField} thisthis
18593 * @param {Mixed} date The date value
18598 * Fires when this field hide.
18599 * @param {Roo.bootstrap.DateField} this
18600 * @param {Mixed} date The date value
18605 * Fires when select a date.
18606 * @param {Roo.bootstrap.DateField} this
18607 * @param {Mixed} date The date value
18613 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18616 * @cfg {String} format
18617 * The default time format string which can be overriden for localization support. The format must be
18618 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18622 onRender: function(ct, position)
18625 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18627 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18629 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18631 this.pop = this.picker().select('>.datepicker-time',true).first();
18632 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18634 this.picker().on('mousedown', this.onMousedown, this);
18635 this.picker().on('click', this.onClick, this);
18637 this.picker().addClass('datepicker-dropdown');
18642 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18643 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18644 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18645 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18646 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18647 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18651 fireKey: function(e){
18652 if (!this.picker().isVisible()){
18653 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18659 e.preventDefault();
18667 this.onTogglePeriod();
18670 this.onIncrementMinutes();
18673 this.onDecrementMinutes();
18682 onClick: function(e) {
18683 e.stopPropagation();
18684 e.preventDefault();
18687 picker : function()
18689 return this.el.select('.datepicker', true).first();
18692 fillTime: function()
18694 var time = this.pop.select('tbody', true).first();
18696 time.dom.innerHTML = '';
18711 cls: 'hours-up glyphicon glyphicon-chevron-up'
18731 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18752 cls: 'timepicker-hour',
18767 cls: 'timepicker-minute',
18782 cls: 'btn btn-primary period',
18804 cls: 'hours-down glyphicon glyphicon-chevron-down'
18824 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18842 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18849 var hours = this.time.getHours();
18850 var minutes = this.time.getMinutes();
18863 hours = hours - 12;
18867 hours = '0' + hours;
18871 minutes = '0' + minutes;
18874 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18875 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18876 this.pop.select('button', true).first().dom.innerHTML = period;
18882 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18884 var cls = ['bottom'];
18886 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18893 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18898 this.picker().addClass(cls.join('-'));
18902 Roo.each(cls, function(c){
18904 _this.picker().setTop(_this.inputEl().getHeight());
18908 _this.picker().setTop(0 - _this.picker().getHeight());
18913 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18917 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18924 onFocus : function()
18926 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18930 onBlur : function()
18932 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18938 this.picker().show();
18943 this.fireEvent('show', this, this.date);
18948 this.picker().hide();
18951 this.fireEvent('hide', this, this.date);
18954 setTime : function()
18957 this.setValue(this.time.format(this.format));
18959 this.fireEvent('select', this, this.date);
18964 onMousedown: function(e){
18965 e.stopPropagation();
18966 e.preventDefault();
18969 onIncrementHours: function()
18971 Roo.log('onIncrementHours');
18972 this.time = this.time.add(Date.HOUR, 1);
18977 onDecrementHours: function()
18979 Roo.log('onDecrementHours');
18980 this.time = this.time.add(Date.HOUR, -1);
18984 onIncrementMinutes: function()
18986 Roo.log('onIncrementMinutes');
18987 this.time = this.time.add(Date.MINUTE, 1);
18991 onDecrementMinutes: function()
18993 Roo.log('onDecrementMinutes');
18994 this.time = this.time.add(Date.MINUTE, -1);
18998 onTogglePeriod: function()
19000 Roo.log('onTogglePeriod');
19001 this.time = this.time.add(Date.HOUR, 12);
19008 Roo.apply(Roo.bootstrap.TimeField, {
19038 cls: 'btn btn-info ok',
19050 Roo.apply(Roo.bootstrap.TimeField, {
19054 cls: 'datepicker dropdown-menu',
19058 cls: 'datepicker-time',
19062 cls: 'table-condensed',
19064 Roo.bootstrap.TimeField.content,
19065 Roo.bootstrap.TimeField.footer
19084 * @class Roo.bootstrap.MonthField
19085 * @extends Roo.bootstrap.Input
19086 * Bootstrap MonthField class
19088 * @cfg {String} language default en
19091 * Create a new MonthField
19092 * @param {Object} config The config object
19095 Roo.bootstrap.MonthField = function(config){
19096 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19101 * Fires when this field show.
19102 * @param {Roo.bootstrap.MonthField} this
19103 * @param {Mixed} date The date value
19108 * Fires when this field hide.
19109 * @param {Roo.bootstrap.MonthField} this
19110 * @param {Mixed} date The date value
19115 * Fires when select a date.
19116 * @param {Roo.bootstrap.MonthField} this
19117 * @param {String} oldvalue The old value
19118 * @param {String} newvalue The new value
19124 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
19126 onRender: function(ct, position)
19129 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19131 this.language = this.language || 'en';
19132 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19133 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19135 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19136 this.isInline = false;
19137 this.isInput = true;
19138 this.component = this.el.select('.add-on', true).first() || false;
19139 this.component = (this.component && this.component.length === 0) ? false : this.component;
19140 this.hasInput = this.component && this.inputEL().length;
19142 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19144 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19146 this.picker().on('mousedown', this.onMousedown, this);
19147 this.picker().on('click', this.onClick, this);
19149 this.picker().addClass('datepicker-dropdown');
19151 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19152 v.setStyle('width', '189px');
19159 if(this.isInline) {
19165 setValue: function(v, suppressEvent)
19167 var o = this.getValue();
19169 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19173 if(suppressEvent !== true){
19174 this.fireEvent('select', this, o, v);
19179 getValue: function()
19184 onClick: function(e)
19186 e.stopPropagation();
19187 e.preventDefault();
19189 var target = e.getTarget();
19191 if(target.nodeName.toLowerCase() === 'i'){
19192 target = Roo.get(target).dom.parentNode;
19195 var nodeName = target.nodeName;
19196 var className = target.className;
19197 var html = target.innerHTML;
19199 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19203 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19205 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19211 picker : function()
19213 return this.pickerEl;
19216 fillMonths: function()
19219 var months = this.picker().select('>.datepicker-months td', true).first();
19221 months.dom.innerHTML = '';
19227 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19230 months.createChild(month);
19239 if(typeof(this.vIndex) == 'undefined' && this.value.length){
19240 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19243 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19244 e.removeClass('active');
19246 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19247 e.addClass('active');
19254 if(this.isInline) {
19258 this.picker().removeClass(['bottom', 'top']);
19260 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19262 * place to the top of element!
19266 this.picker().addClass('top');
19267 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19272 this.picker().addClass('bottom');
19274 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19277 onFocus : function()
19279 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19283 onBlur : function()
19285 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19287 var d = this.inputEl().getValue();
19296 this.picker().show();
19297 this.picker().select('>.datepicker-months', true).first().show();
19301 this.fireEvent('show', this, this.date);
19306 if(this.isInline) {
19309 this.picker().hide();
19310 this.fireEvent('hide', this, this.date);
19314 onMousedown: function(e)
19316 e.stopPropagation();
19317 e.preventDefault();
19322 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19326 fireKey: function(e)
19328 if (!this.picker().isVisible()){
19329 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19340 e.preventDefault();
19344 dir = e.keyCode == 37 ? -1 : 1;
19346 this.vIndex = this.vIndex + dir;
19348 if(this.vIndex < 0){
19352 if(this.vIndex > 11){
19356 if(isNaN(this.vIndex)){
19360 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19366 dir = e.keyCode == 38 ? -1 : 1;
19368 this.vIndex = this.vIndex + dir * 4;
19370 if(this.vIndex < 0){
19374 if(this.vIndex > 11){
19378 if(isNaN(this.vIndex)){
19382 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19387 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19388 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19392 e.preventDefault();
19395 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19396 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19412 this.picker().remove();
19417 Roo.apply(Roo.bootstrap.MonthField, {
19436 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19437 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19442 Roo.apply(Roo.bootstrap.MonthField, {
19446 cls: 'datepicker dropdown-menu roo-dynamic',
19450 cls: 'datepicker-months',
19454 cls: 'table-condensed',
19456 Roo.bootstrap.DateField.content
19476 * @class Roo.bootstrap.CheckBox
19477 * @extends Roo.bootstrap.Input
19478 * Bootstrap CheckBox class
19480 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19481 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19482 * @cfg {String} boxLabel The text that appears beside the checkbox
19483 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19484 * @cfg {Boolean} checked initnal the element
19485 * @cfg {Boolean} inline inline the element (default false)
19486 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19489 * Create a new CheckBox
19490 * @param {Object} config The config object
19493 Roo.bootstrap.CheckBox = function(config){
19494 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19499 * Fires when the element is checked or unchecked.
19500 * @param {Roo.bootstrap.CheckBox} this This input
19501 * @param {Boolean} checked The new checked value
19508 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
19510 inputType: 'checkbox',
19518 getAutoCreate : function()
19520 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19526 cfg.cls = 'form-group ' + this.inputType; //input-group
19529 cfg.cls += ' ' + this.inputType + '-inline';
19535 type : this.inputType,
19536 value : this.inputValue,
19537 cls : 'roo-' + this.inputType, //'form-box',
19538 placeholder : this.placeholder || ''
19542 if(this.inputType != 'radio'){
19546 cls : 'roo-hidden-value',
19547 value : this.checked ? this.valueOff : this.inputValue
19552 if (this.weight) { // Validity check?
19553 cfg.cls += " " + this.inputType + "-" + this.weight;
19556 if (this.disabled) {
19557 input.disabled=true;
19561 input.checked = this.checked;
19568 input.name = this.name;
19570 if(this.inputType != 'radio'){
19571 hidden.name = this.name;
19572 input.name = '_hidden_' + this.name;
19577 input.cls += ' input-' + this.size;
19582 ['xs','sm','md','lg'].map(function(size){
19583 if (settings[size]) {
19584 cfg.cls += ' col-' + size + '-' + settings[size];
19588 var inputblock = input;
19590 if (this.before || this.after) {
19593 cls : 'input-group',
19598 inputblock.cn.push({
19600 cls : 'input-group-addon',
19605 inputblock.cn.push(input);
19607 if(this.inputType != 'radio'){
19608 inputblock.cn.push(hidden);
19612 inputblock.cn.push({
19614 cls : 'input-group-addon',
19621 if (align ==='left' && this.fieldLabel.length) {
19622 // Roo.log("left and has label");
19628 cls : 'control-label col-md-' + this.labelWidth,
19629 html : this.fieldLabel
19633 cls : "col-md-" + (12 - this.labelWidth),
19640 } else if ( this.fieldLabel.length) {
19641 // Roo.log(" label");
19645 tag: this.boxLabel ? 'span' : 'label',
19647 cls: 'control-label box-input-label',
19648 //cls : 'input-group-addon',
19649 html : this.fieldLabel
19659 // Roo.log(" no label && no align");
19660 cfg.cn = [ inputblock ] ;
19666 var boxLabelCfg = {
19668 //'for': id, // box label is handled by onclick - so no for...
19670 html: this.boxLabel
19674 boxLabelCfg.tooltip = this.tooltip;
19677 cfg.cn.push(boxLabelCfg);
19680 if(this.inputType != 'radio'){
19681 cfg.cn.push(hidden);
19689 * return the real input element.
19691 inputEl: function ()
19693 return this.el.select('input.roo-' + this.inputType,true).first();
19695 hiddenEl: function ()
19697 return this.el.select('input.roo-hidden-value',true).first();
19700 labelEl: function()
19702 return this.el.select('label.control-label',true).first();
19704 /* depricated... */
19708 return this.labelEl();
19711 boxLabelEl: function()
19713 return this.el.select('label.box-label',true).first();
19716 initEvents : function()
19718 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19720 this.inputEl().on('click', this.onClick, this);
19722 if (this.boxLabel) {
19723 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19726 this.startValue = this.getValue();
19729 Roo.bootstrap.CheckBox.register(this);
19733 onClick : function()
19735 this.setChecked(!this.checked);
19738 setChecked : function(state,suppressEvent)
19740 this.startValue = this.getValue();
19742 if(this.inputType == 'radio'){
19744 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19745 e.dom.checked = false;
19748 this.inputEl().dom.checked = true;
19750 this.inputEl().dom.value = this.inputValue;
19752 if(suppressEvent !== true){
19753 this.fireEvent('check', this, true);
19761 this.checked = state;
19763 this.inputEl().dom.checked = state;
19766 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19768 if(suppressEvent !== true){
19769 this.fireEvent('check', this, state);
19775 getValue : function()
19777 if(this.inputType == 'radio'){
19778 return this.getGroupValue();
19781 return this.hiddenEl().dom.value;
19785 getGroupValue : function()
19787 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19791 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19794 setValue : function(v,suppressEvent)
19796 if(this.inputType == 'radio'){
19797 this.setGroupValue(v, suppressEvent);
19801 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19806 setGroupValue : function(v, suppressEvent)
19808 this.startValue = this.getValue();
19810 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19811 e.dom.checked = false;
19813 if(e.dom.value == v){
19814 e.dom.checked = true;
19818 if(suppressEvent !== true){
19819 this.fireEvent('check', this, true);
19827 validate : function()
19831 (this.inputType == 'radio' && this.validateRadio()) ||
19832 (this.inputType == 'checkbox' && this.validateCheckbox())
19838 this.markInvalid();
19842 validateRadio : function()
19844 if(this.allowBlank){
19850 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19851 if(!e.dom.checked){
19863 validateCheckbox : function()
19866 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19869 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19877 for(var i in group){
19882 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19889 * Mark this field as valid
19891 markValid : function()
19893 if(this.allowBlank){
19899 this.fireEvent('valid', this);
19901 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19904 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19911 if(this.inputType == 'radio'){
19912 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19913 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19914 e.findParent('.form-group', false, true).addClass(_this.validClass);
19921 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19922 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19926 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19932 for(var i in group){
19933 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19934 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19939 * Mark this field as invalid
19940 * @param {String} msg The validation message
19942 markInvalid : function(msg)
19944 if(this.allowBlank){
19950 this.fireEvent('invalid', this, msg);
19952 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19955 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19959 label.markInvalid();
19962 if(this.inputType == 'radio'){
19963 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19964 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19965 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19972 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19973 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19977 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19983 for(var i in group){
19984 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19985 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19990 disable : function()
19992 if(this.inputType != 'radio'){
19993 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20000 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20001 _this.getActionEl().addClass(this.disabledClass);
20002 e.dom.disabled = true;
20006 this.disabled = true;
20007 this.fireEvent("disable", this);
20011 enable : function()
20013 if(this.inputType != 'radio'){
20014 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20021 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20022 _this.getActionEl().removeClass(this.disabledClass);
20023 e.dom.disabled = false;
20027 this.disabled = false;
20028 this.fireEvent("enable", this);
20034 Roo.apply(Roo.bootstrap.CheckBox, {
20039 * register a CheckBox Group
20040 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20042 register : function(checkbox)
20044 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20045 this.groups[checkbox.groupId] = {};
20048 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20052 this.groups[checkbox.groupId][checkbox.name] = checkbox;
20056 * fetch a CheckBox Group based on the group ID
20057 * @param {string} the group ID
20058 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20060 get: function(groupId) {
20061 if (typeof(this.groups[groupId]) == 'undefined') {
20065 return this.groups[groupId] ;
20077 *<div class="radio">
20079 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
20080 Option one is this and that—be sure to include why it's great
20087 *<label class="radio-inline">fieldLabel</label>
20088 *<label class="radio-inline">
20089 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
20097 * @class Roo.bootstrap.Radio
20098 * @extends Roo.bootstrap.CheckBox
20099 * Bootstrap Radio class
20102 * Create a new Radio
20103 * @param {Object} config The config object
20106 Roo.bootstrap.Radio = function(config){
20107 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20111 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
20113 inputType: 'radio',
20117 getAutoCreate : function()
20119 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20120 align = align || 'left'; // default...
20127 tag : this.inline ? 'span' : 'div',
20128 cls : 'form-group',
20132 var inline = this.inline ? ' radio-inline' : '';
20136 // does not need for, as we wrap the input with it..
20138 cls : 'control-label box-label' + inline,
20141 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
20145 //cls : 'control-label' + inline,
20146 html : this.fieldLabel,
20147 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
20153 type : this.inputType,
20154 //value : (!this.checked) ? this.valueOff : this.inputValue,
20155 value : this.inputValue,
20157 placeholder : this.placeholder || '' // ?? needed????
20160 if (this.weight) { // Validity check?
20161 input.cls += " radio-" + this.weight;
20163 if (this.disabled) {
20164 input.disabled=true;
20168 input.checked = this.checked;
20172 input.name = this.name;
20176 input.cls += ' input-' + this.size;
20179 //?? can span's inline have a width??
20182 ['xs','sm','md','lg'].map(function(size){
20183 if (settings[size]) {
20184 cfg.cls += ' col-' + size + '-' + settings[size];
20188 var inputblock = input;
20190 if (this.before || this.after) {
20193 cls : 'input-group',
20198 inputblock.cn.push({
20200 cls : 'input-group-addon',
20204 inputblock.cn.push(input);
20206 inputblock.cn.push({
20208 cls : 'input-group-addon',
20216 if (this.fieldLabel && this.fieldLabel.length) {
20217 cfg.cn.push(fieldLabel);
20220 // normal bootstrap puts the input inside the label.
20221 // however with our styled version - it has to go after the input.
20223 //lbl.cn.push(inputblock);
20227 cls: 'radio' + inline,
20234 cfg.cn.push( lblwrap);
20239 html: this.boxLabel
20248 initEvents : function()
20250 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20252 this.inputEl().on('click', this.onClick, this);
20253 if (this.boxLabel) {
20254 //Roo.log('find label');
20255 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
20260 inputEl: function ()
20262 return this.el.select('input.roo-radio',true).first();
20264 onClick : function()
20267 this.setChecked(true);
20270 setChecked : function(state,suppressEvent)
20273 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20274 v.dom.checked = false;
20277 Roo.log(this.inputEl().dom);
20278 this.checked = state;
20279 this.inputEl().dom.checked = state;
20281 if(suppressEvent !== true){
20282 this.fireEvent('check', this, state);
20285 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20289 getGroupValue : function()
20292 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20293 if(v.dom.checked == true){
20294 value = v.dom.value;
20302 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
20303 * @return {Mixed} value The field value
20305 getValue : function(){
20306 return this.getGroupValue();
20310 //<script type="text/javascript">
20313 * Based Ext JS Library 1.1.1
20314 * Copyright(c) 2006-2007, Ext JS, LLC.
20320 * @class Roo.HtmlEditorCore
20321 * @extends Roo.Component
20322 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20324 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20327 Roo.HtmlEditorCore = function(config){
20330 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20335 * @event initialize
20336 * Fires when the editor is fully initialized (including the iframe)
20337 * @param {Roo.HtmlEditorCore} this
20342 * Fires when the editor is first receives the focus. Any insertion must wait
20343 * until after this event.
20344 * @param {Roo.HtmlEditorCore} this
20348 * @event beforesync
20349 * Fires before the textarea is updated with content from the editor iframe. Return false
20350 * to cancel the sync.
20351 * @param {Roo.HtmlEditorCore} this
20352 * @param {String} html
20356 * @event beforepush
20357 * Fires before the iframe editor is updated with content from the textarea. Return false
20358 * to cancel the push.
20359 * @param {Roo.HtmlEditorCore} this
20360 * @param {String} html
20365 * Fires when the textarea is updated with content from the editor iframe.
20366 * @param {Roo.HtmlEditorCore} this
20367 * @param {String} html
20372 * Fires when the iframe editor is updated with content from the textarea.
20373 * @param {Roo.HtmlEditorCore} this
20374 * @param {String} html
20379 * @event editorevent
20380 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20381 * @param {Roo.HtmlEditorCore} this
20387 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20389 // defaults : white / black...
20390 this.applyBlacklists();
20397 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20401 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20407 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20412 * @cfg {Number} height (in pixels)
20416 * @cfg {Number} width (in pixels)
20421 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20424 stylesheets: false,
20429 // private properties
20430 validationEvent : false,
20432 initialized : false,
20434 sourceEditMode : false,
20435 onFocus : Roo.emptyFn,
20437 hideMode:'offsets',
20441 // blacklist + whitelisted elements..
20448 * Protected method that will not generally be called directly. It
20449 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20450 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20452 getDocMarkup : function(){
20456 // inherit styels from page...??
20457 if (this.stylesheets === false) {
20459 Roo.get(document.head).select('style').each(function(node) {
20460 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20463 Roo.get(document.head).select('link').each(function(node) {
20464 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20467 } else if (!this.stylesheets.length) {
20469 st = '<style type="text/css">' +
20470 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20476 st += '<style type="text/css">' +
20477 'IMG { cursor: pointer } ' +
20481 return '<html><head>' + st +
20482 //<style type="text/css">' +
20483 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20485 ' </head><body class="roo-htmleditor-body"></body></html>';
20489 onRender : function(ct, position)
20492 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20493 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20496 this.el.dom.style.border = '0 none';
20497 this.el.dom.setAttribute('tabIndex', -1);
20498 this.el.addClass('x-hidden hide');
20502 if(Roo.isIE){ // fix IE 1px bogus margin
20503 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20507 this.frameId = Roo.id();
20511 var iframe = this.owner.wrap.createChild({
20513 cls: 'form-control', // bootstrap..
20515 name: this.frameId,
20516 frameBorder : 'no',
20517 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20522 this.iframe = iframe.dom;
20524 this.assignDocWin();
20526 this.doc.designMode = 'on';
20529 this.doc.write(this.getDocMarkup());
20533 var task = { // must defer to wait for browser to be ready
20535 //console.log("run task?" + this.doc.readyState);
20536 this.assignDocWin();
20537 if(this.doc.body || this.doc.readyState == 'complete'){
20539 this.doc.designMode="on";
20543 Roo.TaskMgr.stop(task);
20544 this.initEditor.defer(10, this);
20551 Roo.TaskMgr.start(task);
20556 onResize : function(w, h)
20558 Roo.log('resize: ' +w + ',' + h );
20559 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20563 if(typeof w == 'number'){
20565 this.iframe.style.width = w + 'px';
20567 if(typeof h == 'number'){
20569 this.iframe.style.height = h + 'px';
20571 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20578 * Toggles the editor between standard and source edit mode.
20579 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20581 toggleSourceEdit : function(sourceEditMode){
20583 this.sourceEditMode = sourceEditMode === true;
20585 if(this.sourceEditMode){
20587 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20590 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20591 //this.iframe.className = '';
20594 //this.setSize(this.owner.wrap.getSize());
20595 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20602 * Protected method that will not generally be called directly. If you need/want
20603 * custom HTML cleanup, this is the method you should override.
20604 * @param {String} html The HTML to be cleaned
20605 * return {String} The cleaned HTML
20607 cleanHtml : function(html){
20608 html = String(html);
20609 if(html.length > 5){
20610 if(Roo.isSafari){ // strip safari nonsense
20611 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20614 if(html == ' '){
20621 * HTML Editor -> Textarea
20622 * Protected method that will not generally be called directly. Syncs the contents
20623 * of the editor iframe with the textarea.
20625 syncValue : function(){
20626 if(this.initialized){
20627 var bd = (this.doc.body || this.doc.documentElement);
20628 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20629 var html = bd.innerHTML;
20631 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20632 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20634 html = '<div style="'+m[0]+'">' + html + '</div>';
20637 html = this.cleanHtml(html);
20638 // fix up the special chars.. normaly like back quotes in word...
20639 // however we do not want to do this with chinese..
20640 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20641 var cc = b.charCodeAt();
20643 (cc >= 0x4E00 && cc < 0xA000 ) ||
20644 (cc >= 0x3400 && cc < 0x4E00 ) ||
20645 (cc >= 0xf900 && cc < 0xfb00 )
20651 if(this.owner.fireEvent('beforesync', this, html) !== false){
20652 this.el.dom.value = html;
20653 this.owner.fireEvent('sync', this, html);
20659 * Protected method that will not generally be called directly. Pushes the value of the textarea
20660 * into the iframe editor.
20662 pushValue : function(){
20663 if(this.initialized){
20664 var v = this.el.dom.value.trim();
20666 // if(v.length < 1){
20670 if(this.owner.fireEvent('beforepush', this, v) !== false){
20671 var d = (this.doc.body || this.doc.documentElement);
20673 this.cleanUpPaste();
20674 this.el.dom.value = d.innerHTML;
20675 this.owner.fireEvent('push', this, v);
20681 deferFocus : function(){
20682 this.focus.defer(10, this);
20686 focus : function(){
20687 if(this.win && !this.sourceEditMode){
20694 assignDocWin: function()
20696 var iframe = this.iframe;
20699 this.doc = iframe.contentWindow.document;
20700 this.win = iframe.contentWindow;
20702 // if (!Roo.get(this.frameId)) {
20705 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20706 // this.win = Roo.get(this.frameId).dom.contentWindow;
20708 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20712 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20713 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20718 initEditor : function(){
20719 //console.log("INIT EDITOR");
20720 this.assignDocWin();
20724 this.doc.designMode="on";
20726 this.doc.write(this.getDocMarkup());
20729 var dbody = (this.doc.body || this.doc.documentElement);
20730 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20731 // this copies styles from the containing element into thsi one..
20732 // not sure why we need all of this..
20733 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20735 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20736 //ss['background-attachment'] = 'fixed'; // w3c
20737 dbody.bgProperties = 'fixed'; // ie
20738 //Roo.DomHelper.applyStyles(dbody, ss);
20739 Roo.EventManager.on(this.doc, {
20740 //'mousedown': this.onEditorEvent,
20741 'mouseup': this.onEditorEvent,
20742 'dblclick': this.onEditorEvent,
20743 'click': this.onEditorEvent,
20744 'keyup': this.onEditorEvent,
20749 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20751 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20752 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20754 this.initialized = true;
20756 this.owner.fireEvent('initialize', this);
20761 onDestroy : function(){
20767 //for (var i =0; i < this.toolbars.length;i++) {
20768 // // fixme - ask toolbars for heights?
20769 // this.toolbars[i].onDestroy();
20772 //this.wrap.dom.innerHTML = '';
20773 //this.wrap.remove();
20778 onFirstFocus : function(){
20780 this.assignDocWin();
20783 this.activated = true;
20786 if(Roo.isGecko){ // prevent silly gecko errors
20788 var s = this.win.getSelection();
20789 if(!s.focusNode || s.focusNode.nodeType != 3){
20790 var r = s.getRangeAt(0);
20791 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20796 this.execCmd('useCSS', true);
20797 this.execCmd('styleWithCSS', false);
20800 this.owner.fireEvent('activate', this);
20804 adjustFont: function(btn){
20805 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20806 //if(Roo.isSafari){ // safari
20809 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20810 if(Roo.isSafari){ // safari
20811 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20812 v = (v < 10) ? 10 : v;
20813 v = (v > 48) ? 48 : v;
20814 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20819 v = Math.max(1, v+adjust);
20821 this.execCmd('FontSize', v );
20824 onEditorEvent : function(e)
20826 this.owner.fireEvent('editorevent', this, e);
20827 // this.updateToolbar();
20828 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20831 insertTag : function(tg)
20833 // could be a bit smarter... -> wrap the current selected tRoo..
20834 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20836 range = this.createRange(this.getSelection());
20837 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20838 wrappingNode.appendChild(range.extractContents());
20839 range.insertNode(wrappingNode);
20846 this.execCmd("formatblock", tg);
20850 insertText : function(txt)
20854 var range = this.createRange();
20855 range.deleteContents();
20856 //alert(Sender.getAttribute('label'));
20858 range.insertNode(this.doc.createTextNode(txt));
20864 * Executes a Midas editor command on the editor document and performs necessary focus and
20865 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20866 * @param {String} cmd The Midas command
20867 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20869 relayCmd : function(cmd, value){
20871 this.execCmd(cmd, value);
20872 this.owner.fireEvent('editorevent', this);
20873 //this.updateToolbar();
20874 this.owner.deferFocus();
20878 * Executes a Midas editor command directly on the editor document.
20879 * For visual commands, you should use {@link #relayCmd} instead.
20880 * <b>This should only be called after the editor is initialized.</b>
20881 * @param {String} cmd The Midas command
20882 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20884 execCmd : function(cmd, value){
20885 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20892 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20894 * @param {String} text | dom node..
20896 insertAtCursor : function(text)
20901 if(!this.activated){
20907 var r = this.doc.selection.createRange();
20918 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20922 // from jquery ui (MIT licenced)
20924 var win = this.win;
20926 if (win.getSelection && win.getSelection().getRangeAt) {
20927 range = win.getSelection().getRangeAt(0);
20928 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20929 range.insertNode(node);
20930 } else if (win.document.selection && win.document.selection.createRange) {
20931 // no firefox support
20932 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20933 win.document.selection.createRange().pasteHTML(txt);
20935 // no firefox support
20936 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20937 this.execCmd('InsertHTML', txt);
20946 mozKeyPress : function(e){
20948 var c = e.getCharCode(), cmd;
20951 c = String.fromCharCode(c).toLowerCase();
20965 this.cleanUpPaste.defer(100, this);
20973 e.preventDefault();
20981 fixKeys : function(){ // load time branching for fastest keydown performance
20983 return function(e){
20984 var k = e.getKey(), r;
20987 r = this.doc.selection.createRange();
20990 r.pasteHTML('    ');
20997 r = this.doc.selection.createRange();
20999 var target = r.parentElement();
21000 if(!target || target.tagName.toLowerCase() != 'li'){
21002 r.pasteHTML('<br />');
21008 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21009 this.cleanUpPaste.defer(100, this);
21015 }else if(Roo.isOpera){
21016 return function(e){
21017 var k = e.getKey();
21021 this.execCmd('InsertHTML','    ');
21024 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21025 this.cleanUpPaste.defer(100, this);
21030 }else if(Roo.isSafari){
21031 return function(e){
21032 var k = e.getKey();
21036 this.execCmd('InsertText','\t');
21040 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21041 this.cleanUpPaste.defer(100, this);
21049 getAllAncestors: function()
21051 var p = this.getSelectedNode();
21054 a.push(p); // push blank onto stack..
21055 p = this.getParentElement();
21059 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21063 a.push(this.doc.body);
21067 lastSelNode : false,
21070 getSelection : function()
21072 this.assignDocWin();
21073 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21076 getSelectedNode: function()
21078 // this may only work on Gecko!!!
21080 // should we cache this!!!!
21085 var range = this.createRange(this.getSelection()).cloneRange();
21088 var parent = range.parentElement();
21090 var testRange = range.duplicate();
21091 testRange.moveToElementText(parent);
21092 if (testRange.inRange(range)) {
21095 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21098 parent = parent.parentElement;
21103 // is ancestor a text element.
21104 var ac = range.commonAncestorContainer;
21105 if (ac.nodeType == 3) {
21106 ac = ac.parentNode;
21109 var ar = ac.childNodes;
21112 var other_nodes = [];
21113 var has_other_nodes = false;
21114 for (var i=0;i<ar.length;i++) {
21115 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21118 // fullly contained node.
21120 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21125 // probably selected..
21126 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21127 other_nodes.push(ar[i]);
21131 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21136 has_other_nodes = true;
21138 if (!nodes.length && other_nodes.length) {
21139 nodes= other_nodes;
21141 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21147 createRange: function(sel)
21149 // this has strange effects when using with
21150 // top toolbar - not sure if it's a great idea.
21151 //this.editor.contentWindow.focus();
21152 if (typeof sel != "undefined") {
21154 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21156 return this.doc.createRange();
21159 return this.doc.createRange();
21162 getParentElement: function()
21165 this.assignDocWin();
21166 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21168 var range = this.createRange(sel);
21171 var p = range.commonAncestorContainer;
21172 while (p.nodeType == 3) { // text node
21183 * Range intersection.. the hard stuff...
21187 * [ -- selected range --- ]
21191 * if end is before start or hits it. fail.
21192 * if start is after end or hits it fail.
21194 * if either hits (but other is outside. - then it's not
21200 // @see http://www.thismuchiknow.co.uk/?p=64.
21201 rangeIntersectsNode : function(range, node)
21203 var nodeRange = node.ownerDocument.createRange();
21205 nodeRange.selectNode(node);
21207 nodeRange.selectNodeContents(node);
21210 var rangeStartRange = range.cloneRange();
21211 rangeStartRange.collapse(true);
21213 var rangeEndRange = range.cloneRange();
21214 rangeEndRange.collapse(false);
21216 var nodeStartRange = nodeRange.cloneRange();
21217 nodeStartRange.collapse(true);
21219 var nodeEndRange = nodeRange.cloneRange();
21220 nodeEndRange.collapse(false);
21222 return rangeStartRange.compareBoundaryPoints(
21223 Range.START_TO_START, nodeEndRange) == -1 &&
21224 rangeEndRange.compareBoundaryPoints(
21225 Range.START_TO_START, nodeStartRange) == 1;
21229 rangeCompareNode : function(range, node)
21231 var nodeRange = node.ownerDocument.createRange();
21233 nodeRange.selectNode(node);
21235 nodeRange.selectNodeContents(node);
21239 range.collapse(true);
21241 nodeRange.collapse(true);
21243 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21244 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
21246 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21248 var nodeIsBefore = ss == 1;
21249 var nodeIsAfter = ee == -1;
21251 if (nodeIsBefore && nodeIsAfter) {
21254 if (!nodeIsBefore && nodeIsAfter) {
21255 return 1; //right trailed.
21258 if (nodeIsBefore && !nodeIsAfter) {
21259 return 2; // left trailed.
21265 // private? - in a new class?
21266 cleanUpPaste : function()
21268 // cleans up the whole document..
21269 Roo.log('cleanuppaste');
21271 this.cleanUpChildren(this.doc.body);
21272 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21273 if (clean != this.doc.body.innerHTML) {
21274 this.doc.body.innerHTML = clean;
21279 cleanWordChars : function(input) {// change the chars to hex code
21280 var he = Roo.HtmlEditorCore;
21282 var output = input;
21283 Roo.each(he.swapCodes, function(sw) {
21284 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21286 output = output.replace(swapper, sw[1]);
21293 cleanUpChildren : function (n)
21295 if (!n.childNodes.length) {
21298 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21299 this.cleanUpChild(n.childNodes[i]);
21306 cleanUpChild : function (node)
21309 //console.log(node);
21310 if (node.nodeName == "#text") {
21311 // clean up silly Windows -- stuff?
21314 if (node.nodeName == "#comment") {
21315 node.parentNode.removeChild(node);
21316 // clean up silly Windows -- stuff?
21319 var lcname = node.tagName.toLowerCase();
21320 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21321 // whitelist of tags..
21323 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21325 node.parentNode.removeChild(node);
21330 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21332 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21333 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21335 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21336 // remove_keep_children = true;
21339 if (remove_keep_children) {
21340 this.cleanUpChildren(node);
21341 // inserts everything just before this node...
21342 while (node.childNodes.length) {
21343 var cn = node.childNodes[0];
21344 node.removeChild(cn);
21345 node.parentNode.insertBefore(cn, node);
21347 node.parentNode.removeChild(node);
21351 if (!node.attributes || !node.attributes.length) {
21352 this.cleanUpChildren(node);
21356 function cleanAttr(n,v)
21359 if (v.match(/^\./) || v.match(/^\//)) {
21362 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21365 if (v.match(/^#/)) {
21368 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21369 node.removeAttribute(n);
21373 var cwhite = this.cwhite;
21374 var cblack = this.cblack;
21376 function cleanStyle(n,v)
21378 if (v.match(/expression/)) { //XSS?? should we even bother..
21379 node.removeAttribute(n);
21383 var parts = v.split(/;/);
21386 Roo.each(parts, function(p) {
21387 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21391 var l = p.split(':').shift().replace(/\s+/g,'');
21392 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21394 if ( cwhite.length && cblack.indexOf(l) > -1) {
21395 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21396 //node.removeAttribute(n);
21400 // only allow 'c whitelisted system attributes'
21401 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21402 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21403 //node.removeAttribute(n);
21413 if (clean.length) {
21414 node.setAttribute(n, clean.join(';'));
21416 node.removeAttribute(n);
21422 for (var i = node.attributes.length-1; i > -1 ; i--) {
21423 var a = node.attributes[i];
21426 if (a.name.toLowerCase().substr(0,2)=='on') {
21427 node.removeAttribute(a.name);
21430 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21431 node.removeAttribute(a.name);
21434 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21435 cleanAttr(a.name,a.value); // fixme..
21438 if (a.name == 'style') {
21439 cleanStyle(a.name,a.value);
21442 /// clean up MS crap..
21443 // tecnically this should be a list of valid class'es..
21446 if (a.name == 'class') {
21447 if (a.value.match(/^Mso/)) {
21448 node.className = '';
21451 if (a.value.match(/body/)) {
21452 node.className = '';
21463 this.cleanUpChildren(node);
21469 * Clean up MS wordisms...
21471 cleanWord : function(node)
21476 this.cleanWord(this.doc.body);
21479 if (node.nodeName == "#text") {
21480 // clean up silly Windows -- stuff?
21483 if (node.nodeName == "#comment") {
21484 node.parentNode.removeChild(node);
21485 // clean up silly Windows -- stuff?
21489 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21490 node.parentNode.removeChild(node);
21494 // remove - but keep children..
21495 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21496 while (node.childNodes.length) {
21497 var cn = node.childNodes[0];
21498 node.removeChild(cn);
21499 node.parentNode.insertBefore(cn, node);
21501 node.parentNode.removeChild(node);
21502 this.iterateChildren(node, this.cleanWord);
21506 if (node.className.length) {
21508 var cn = node.className.split(/\W+/);
21510 Roo.each(cn, function(cls) {
21511 if (cls.match(/Mso[a-zA-Z]+/)) {
21516 node.className = cna.length ? cna.join(' ') : '';
21518 node.removeAttribute("class");
21522 if (node.hasAttribute("lang")) {
21523 node.removeAttribute("lang");
21526 if (node.hasAttribute("style")) {
21528 var styles = node.getAttribute("style").split(";");
21530 Roo.each(styles, function(s) {
21531 if (!s.match(/:/)) {
21534 var kv = s.split(":");
21535 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21538 // what ever is left... we allow.
21541 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21542 if (!nstyle.length) {
21543 node.removeAttribute('style');
21546 this.iterateChildren(node, this.cleanWord);
21552 * iterateChildren of a Node, calling fn each time, using this as the scole..
21553 * @param {DomNode} node node to iterate children of.
21554 * @param {Function} fn method of this class to call on each item.
21556 iterateChildren : function(node, fn)
21558 if (!node.childNodes.length) {
21561 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21562 fn.call(this, node.childNodes[i])
21568 * cleanTableWidths.
21570 * Quite often pasting from word etc.. results in tables with column and widths.
21571 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21574 cleanTableWidths : function(node)
21579 this.cleanTableWidths(this.doc.body);
21584 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21587 Roo.log(node.tagName);
21588 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21589 this.iterateChildren(node, this.cleanTableWidths);
21592 if (node.hasAttribute('width')) {
21593 node.removeAttribute('width');
21597 if (node.hasAttribute("style")) {
21600 var styles = node.getAttribute("style").split(";");
21602 Roo.each(styles, function(s) {
21603 if (!s.match(/:/)) {
21606 var kv = s.split(":");
21607 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21610 // what ever is left... we allow.
21613 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21614 if (!nstyle.length) {
21615 node.removeAttribute('style');
21619 this.iterateChildren(node, this.cleanTableWidths);
21627 domToHTML : function(currentElement, depth, nopadtext) {
21629 depth = depth || 0;
21630 nopadtext = nopadtext || false;
21632 if (!currentElement) {
21633 return this.domToHTML(this.doc.body);
21636 //Roo.log(currentElement);
21638 var allText = false;
21639 var nodeName = currentElement.nodeName;
21640 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21642 if (nodeName == '#text') {
21644 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21649 if (nodeName != 'BODY') {
21652 // Prints the node tagName, such as <A>, <IMG>, etc
21655 for(i = 0; i < currentElement.attributes.length;i++) {
21657 var aname = currentElement.attributes.item(i).name;
21658 if (!currentElement.attributes.item(i).value.length) {
21661 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21664 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21673 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21676 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21681 // Traverse the tree
21683 var currentElementChild = currentElement.childNodes.item(i);
21684 var allText = true;
21685 var innerHTML = '';
21687 while (currentElementChild) {
21688 // Formatting code (indent the tree so it looks nice on the screen)
21689 var nopad = nopadtext;
21690 if (lastnode == 'SPAN') {
21694 if (currentElementChild.nodeName == '#text') {
21695 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21696 toadd = nopadtext ? toadd : toadd.trim();
21697 if (!nopad && toadd.length > 80) {
21698 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21700 innerHTML += toadd;
21703 currentElementChild = currentElement.childNodes.item(i);
21709 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21711 // Recursively traverse the tree structure of the child node
21712 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21713 lastnode = currentElementChild.nodeName;
21715 currentElementChild=currentElement.childNodes.item(i);
21721 // The remaining code is mostly for formatting the tree
21722 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21727 ret+= "</"+tagName+">";
21733 applyBlacklists : function()
21735 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21736 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21740 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21741 if (b.indexOf(tag) > -1) {
21744 this.white.push(tag);
21748 Roo.each(w, function(tag) {
21749 if (b.indexOf(tag) > -1) {
21752 if (this.white.indexOf(tag) > -1) {
21755 this.white.push(tag);
21760 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21761 if (w.indexOf(tag) > -1) {
21764 this.black.push(tag);
21768 Roo.each(b, function(tag) {
21769 if (w.indexOf(tag) > -1) {
21772 if (this.black.indexOf(tag) > -1) {
21775 this.black.push(tag);
21780 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21781 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21785 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21786 if (b.indexOf(tag) > -1) {
21789 this.cwhite.push(tag);
21793 Roo.each(w, function(tag) {
21794 if (b.indexOf(tag) > -1) {
21797 if (this.cwhite.indexOf(tag) > -1) {
21800 this.cwhite.push(tag);
21805 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21806 if (w.indexOf(tag) > -1) {
21809 this.cblack.push(tag);
21813 Roo.each(b, function(tag) {
21814 if (w.indexOf(tag) > -1) {
21817 if (this.cblack.indexOf(tag) > -1) {
21820 this.cblack.push(tag);
21825 setStylesheets : function(stylesheets)
21827 if(typeof(stylesheets) == 'string'){
21828 Roo.get(this.iframe.contentDocument.head).createChild({
21830 rel : 'stylesheet',
21839 Roo.each(stylesheets, function(s) {
21844 Roo.get(_this.iframe.contentDocument.head).createChild({
21846 rel : 'stylesheet',
21855 removeStylesheets : function()
21859 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21864 // hide stuff that is not compatible
21878 * @event specialkey
21882 * @cfg {String} fieldClass @hide
21885 * @cfg {String} focusClass @hide
21888 * @cfg {String} autoCreate @hide
21891 * @cfg {String} inputType @hide
21894 * @cfg {String} invalidClass @hide
21897 * @cfg {String} invalidText @hide
21900 * @cfg {String} msgFx @hide
21903 * @cfg {String} validateOnBlur @hide
21907 Roo.HtmlEditorCore.white = [
21908 'area', 'br', 'img', 'input', 'hr', 'wbr',
21910 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21911 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21912 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21913 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21914 'table', 'ul', 'xmp',
21916 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21919 'dir', 'menu', 'ol', 'ul', 'dl',
21925 Roo.HtmlEditorCore.black = [
21926 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21928 'base', 'basefont', 'bgsound', 'blink', 'body',
21929 'frame', 'frameset', 'head', 'html', 'ilayer',
21930 'iframe', 'layer', 'link', 'meta', 'object',
21931 'script', 'style' ,'title', 'xml' // clean later..
21933 Roo.HtmlEditorCore.clean = [
21934 'script', 'style', 'title', 'xml'
21936 Roo.HtmlEditorCore.remove = [
21941 Roo.HtmlEditorCore.ablack = [
21945 Roo.HtmlEditorCore.aclean = [
21946 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21950 Roo.HtmlEditorCore.pwhite= [
21951 'http', 'https', 'mailto'
21954 // white listed style attributes.
21955 Roo.HtmlEditorCore.cwhite= [
21956 // 'text-align', /// default is to allow most things..
21962 // black listed style attributes.
21963 Roo.HtmlEditorCore.cblack= [
21964 // 'font-size' -- this can be set by the project
21968 Roo.HtmlEditorCore.swapCodes =[
21987 * @class Roo.bootstrap.HtmlEditor
21988 * @extends Roo.bootstrap.TextArea
21989 * Bootstrap HtmlEditor class
21992 * Create a new HtmlEditor
21993 * @param {Object} config The config object
21996 Roo.bootstrap.HtmlEditor = function(config){
21997 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21998 if (!this.toolbars) {
21999 this.toolbars = [];
22001 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22004 * @event initialize
22005 * Fires when the editor is fully initialized (including the iframe)
22006 * @param {HtmlEditor} this
22011 * Fires when the editor is first receives the focus. Any insertion must wait
22012 * until after this event.
22013 * @param {HtmlEditor} this
22017 * @event beforesync
22018 * Fires before the textarea is updated with content from the editor iframe. Return false
22019 * to cancel the sync.
22020 * @param {HtmlEditor} this
22021 * @param {String} html
22025 * @event beforepush
22026 * Fires before the iframe editor is updated with content from the textarea. Return false
22027 * to cancel the push.
22028 * @param {HtmlEditor} this
22029 * @param {String} html
22034 * Fires when the textarea is updated with content from the editor iframe.
22035 * @param {HtmlEditor} this
22036 * @param {String} html
22041 * Fires when the iframe editor is updated with content from the textarea.
22042 * @param {HtmlEditor} this
22043 * @param {String} html
22047 * @event editmodechange
22048 * Fires when the editor switches edit modes
22049 * @param {HtmlEditor} this
22050 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22052 editmodechange: true,
22054 * @event editorevent
22055 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22056 * @param {HtmlEditor} this
22060 * @event firstfocus
22061 * Fires when on first focus - needed by toolbars..
22062 * @param {HtmlEditor} this
22067 * Auto save the htmlEditor value as a file into Events
22068 * @param {HtmlEditor} this
22072 * @event savedpreview
22073 * preview the saved version of htmlEditor
22074 * @param {HtmlEditor} this
22081 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
22085 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22090 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22095 * @cfg {Number} height (in pixels)
22099 * @cfg {Number} width (in pixels)
22104 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22107 stylesheets: false,
22112 // private properties
22113 validationEvent : false,
22115 initialized : false,
22118 onFocus : Roo.emptyFn,
22120 hideMode:'offsets',
22123 tbContainer : false,
22125 toolbarContainer :function() {
22126 return this.wrap.select('.x-html-editor-tb',true).first();
22130 * Protected method that will not generally be called directly. It
22131 * is called when the editor creates its toolbar. Override this method if you need to
22132 * add custom toolbar buttons.
22133 * @param {HtmlEditor} editor
22135 createToolbar : function(){
22137 Roo.log("create toolbars");
22139 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22140 this.toolbars[0].render(this.toolbarContainer());
22144 // if (!editor.toolbars || !editor.toolbars.length) {
22145 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22148 // for (var i =0 ; i < editor.toolbars.length;i++) {
22149 // editor.toolbars[i] = Roo.factory(
22150 // typeof(editor.toolbars[i]) == 'string' ?
22151 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
22152 // Roo.bootstrap.HtmlEditor);
22153 // editor.toolbars[i].init(editor);
22159 onRender : function(ct, position)
22161 // Roo.log("Call onRender: " + this.xtype);
22163 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22165 this.wrap = this.inputEl().wrap({
22166 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22169 this.editorcore.onRender(ct, position);
22171 if (this.resizable) {
22172 this.resizeEl = new Roo.Resizable(this.wrap, {
22176 minHeight : this.height,
22177 height: this.height,
22178 handles : this.resizable,
22181 resize : function(r, w, h) {
22182 _t.onResize(w,h); // -something
22188 this.createToolbar(this);
22191 if(!this.width && this.resizable){
22192 this.setSize(this.wrap.getSize());
22194 if (this.resizeEl) {
22195 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22196 // should trigger onReize..
22202 onResize : function(w, h)
22204 Roo.log('resize: ' +w + ',' + h );
22205 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22209 if(this.inputEl() ){
22210 if(typeof w == 'number'){
22211 var aw = w - this.wrap.getFrameWidth('lr');
22212 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22215 if(typeof h == 'number'){
22216 var tbh = -11; // fixme it needs to tool bar size!
22217 for (var i =0; i < this.toolbars.length;i++) {
22218 // fixme - ask toolbars for heights?
22219 tbh += this.toolbars[i].el.getHeight();
22220 //if (this.toolbars[i].footer) {
22221 // tbh += this.toolbars[i].footer.el.getHeight();
22229 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22230 ah -= 5; // knock a few pixes off for look..
22231 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22235 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22236 this.editorcore.onResize(ew,eh);
22241 * Toggles the editor between standard and source edit mode.
22242 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22244 toggleSourceEdit : function(sourceEditMode)
22246 this.editorcore.toggleSourceEdit(sourceEditMode);
22248 if(this.editorcore.sourceEditMode){
22249 Roo.log('editor - showing textarea');
22252 // Roo.log(this.syncValue());
22254 this.inputEl().removeClass(['hide', 'x-hidden']);
22255 this.inputEl().dom.removeAttribute('tabIndex');
22256 this.inputEl().focus();
22258 Roo.log('editor - hiding textarea');
22260 // Roo.log(this.pushValue());
22263 this.inputEl().addClass(['hide', 'x-hidden']);
22264 this.inputEl().dom.setAttribute('tabIndex', -1);
22265 //this.deferFocus();
22268 if(this.resizable){
22269 this.setSize(this.wrap.getSize());
22272 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22275 // private (for BoxComponent)
22276 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22278 // private (for BoxComponent)
22279 getResizeEl : function(){
22283 // private (for BoxComponent)
22284 getPositionEl : function(){
22289 initEvents : function(){
22290 this.originalValue = this.getValue();
22294 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22297 // markInvalid : Roo.emptyFn,
22299 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22302 // clearInvalid : Roo.emptyFn,
22304 setValue : function(v){
22305 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22306 this.editorcore.pushValue();
22311 deferFocus : function(){
22312 this.focus.defer(10, this);
22316 focus : function(){
22317 this.editorcore.focus();
22323 onDestroy : function(){
22329 for (var i =0; i < this.toolbars.length;i++) {
22330 // fixme - ask toolbars for heights?
22331 this.toolbars[i].onDestroy();
22334 this.wrap.dom.innerHTML = '';
22335 this.wrap.remove();
22340 onFirstFocus : function(){
22341 //Roo.log("onFirstFocus");
22342 this.editorcore.onFirstFocus();
22343 for (var i =0; i < this.toolbars.length;i++) {
22344 this.toolbars[i].onFirstFocus();
22350 syncValue : function()
22352 this.editorcore.syncValue();
22355 pushValue : function()
22357 this.editorcore.pushValue();
22361 // hide stuff that is not compatible
22375 * @event specialkey
22379 * @cfg {String} fieldClass @hide
22382 * @cfg {String} focusClass @hide
22385 * @cfg {String} autoCreate @hide
22388 * @cfg {String} inputType @hide
22391 * @cfg {String} invalidClass @hide
22394 * @cfg {String} invalidText @hide
22397 * @cfg {String} msgFx @hide
22400 * @cfg {String} validateOnBlur @hide
22409 Roo.namespace('Roo.bootstrap.htmleditor');
22411 * @class Roo.bootstrap.HtmlEditorToolbar1
22416 new Roo.bootstrap.HtmlEditor({
22419 new Roo.bootstrap.HtmlEditorToolbar1({
22420 disable : { fonts: 1 , format: 1, ..., ... , ...],
22426 * @cfg {Object} disable List of elements to disable..
22427 * @cfg {Array} btns List of additional buttons.
22431 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22434 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22437 Roo.apply(this, config);
22439 // default disabled, based on 'good practice'..
22440 this.disable = this.disable || {};
22441 Roo.applyIf(this.disable, {
22444 specialElements : true
22446 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22448 this.editor = config.editor;
22449 this.editorcore = config.editor.editorcore;
22451 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22453 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22454 // dont call parent... till later.
22456 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
22461 editorcore : false,
22466 "h1","h2","h3","h4","h5","h6",
22468 "abbr", "acronym", "address", "cite", "samp", "var",
22472 onRender : function(ct, position)
22474 // Roo.log("Call onRender: " + this.xtype);
22476 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22478 this.el.dom.style.marginBottom = '0';
22480 var editorcore = this.editorcore;
22481 var editor= this.editor;
22484 var btn = function(id,cmd , toggle, handler){
22486 var event = toggle ? 'toggle' : 'click';
22491 xns: Roo.bootstrap,
22494 enableToggle:toggle !== false,
22496 pressed : toggle ? false : null,
22499 a.listeners[toggle ? 'toggle' : 'click'] = function() {
22500 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
22509 xns: Roo.bootstrap,
22510 glyphicon : 'font',
22514 xns: Roo.bootstrap,
22518 Roo.each(this.formats, function(f) {
22519 style.menu.items.push({
22521 xns: Roo.bootstrap,
22522 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22527 editorcore.insertTag(this.tagname);
22534 children.push(style);
22537 btn('bold',false,true);
22538 btn('italic',false,true);
22539 btn('align-left', 'justifyleft',true);
22540 btn('align-center', 'justifycenter',true);
22541 btn('align-right' , 'justifyright',true);
22542 btn('link', false, false, function(btn) {
22543 //Roo.log("create link?");
22544 var url = prompt(this.createLinkText, this.defaultLinkValue);
22545 if(url && url != 'http:/'+'/'){
22546 this.editorcore.relayCmd('createlink', url);
22549 btn('list','insertunorderedlist',true);
22550 btn('pencil', false,true, function(btn){
22553 this.toggleSourceEdit(btn.pressed);
22559 xns: Roo.bootstrap,
22564 xns: Roo.bootstrap,
22569 cog.menu.items.push({
22571 xns: Roo.bootstrap,
22572 html : Clean styles,
22577 editorcore.insertTag(this.tagname);
22586 this.xtype = 'NavSimplebar';
22588 for(var i=0;i< children.length;i++) {
22590 this.buttons.add(this.addxtypeChild(children[i]));
22594 editor.on('editorevent', this.updateToolbar, this);
22596 onBtnClick : function(id)
22598 this.editorcore.relayCmd(id);
22599 this.editorcore.focus();
22603 * Protected method that will not generally be called directly. It triggers
22604 * a toolbar update by reading the markup state of the current selection in the editor.
22606 updateToolbar: function(){
22608 if(!this.editorcore.activated){
22609 this.editor.onFirstFocus(); // is this neeed?
22613 var btns = this.buttons;
22614 var doc = this.editorcore.doc;
22615 btns.get('bold').setActive(doc.queryCommandState('bold'));
22616 btns.get('italic').setActive(doc.queryCommandState('italic'));
22617 //btns.get('underline').setActive(doc.queryCommandState('underline'));
22619 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22620 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22621 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22623 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22624 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22627 var ans = this.editorcore.getAllAncestors();
22628 if (this.formatCombo) {
22631 var store = this.formatCombo.store;
22632 this.formatCombo.setValue("");
22633 for (var i =0; i < ans.length;i++) {
22634 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22636 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22644 // hides menus... - so this cant be on a menu...
22645 Roo.bootstrap.MenuMgr.hideAll();
22647 Roo.bootstrap.MenuMgr.hideAll();
22648 //this.editorsyncValue();
22650 onFirstFocus: function() {
22651 this.buttons.each(function(item){
22655 toggleSourceEdit : function(sourceEditMode){
22658 if(sourceEditMode){
22659 Roo.log("disabling buttons");
22660 this.buttons.each( function(item){
22661 if(item.cmd != 'pencil'){
22667 Roo.log("enabling buttons");
22668 if(this.editorcore.initialized){
22669 this.buttons.each( function(item){
22675 Roo.log("calling toggole on editor");
22676 // tell the editor that it's been pressed..
22677 this.editor.toggleSourceEdit(sourceEditMode);
22687 * @class Roo.bootstrap.Table.AbstractSelectionModel
22688 * @extends Roo.util.Observable
22689 * Abstract base class for grid SelectionModels. It provides the interface that should be
22690 * implemented by descendant classes. This class should not be directly instantiated.
22693 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22694 this.locked = false;
22695 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22699 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22700 /** @ignore Called by the grid automatically. Do not call directly. */
22701 init : function(grid){
22707 * Locks the selections.
22710 this.locked = true;
22714 * Unlocks the selections.
22716 unlock : function(){
22717 this.locked = false;
22721 * Returns true if the selections are locked.
22722 * @return {Boolean}
22724 isLocked : function(){
22725 return this.locked;
22729 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22730 * @class Roo.bootstrap.Table.RowSelectionModel
22731 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22732 * It supports multiple selections and keyboard selection/navigation.
22734 * @param {Object} config
22737 Roo.bootstrap.Table.RowSelectionModel = function(config){
22738 Roo.apply(this, config);
22739 this.selections = new Roo.util.MixedCollection(false, function(o){
22744 this.lastActive = false;
22748 * @event selectionchange
22749 * Fires when the selection changes
22750 * @param {SelectionModel} this
22752 "selectionchange" : true,
22754 * @event afterselectionchange
22755 * Fires after the selection changes (eg. by key press or clicking)
22756 * @param {SelectionModel} this
22758 "afterselectionchange" : true,
22760 * @event beforerowselect
22761 * Fires when a row is selected being selected, return false to cancel.
22762 * @param {SelectionModel} this
22763 * @param {Number} rowIndex The selected index
22764 * @param {Boolean} keepExisting False if other selections will be cleared
22766 "beforerowselect" : true,
22769 * Fires when a row is selected.
22770 * @param {SelectionModel} this
22771 * @param {Number} rowIndex The selected index
22772 * @param {Roo.data.Record} r The record
22774 "rowselect" : true,
22776 * @event rowdeselect
22777 * Fires when a row is deselected.
22778 * @param {SelectionModel} this
22779 * @param {Number} rowIndex The selected index
22781 "rowdeselect" : true
22783 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22784 this.locked = false;
22787 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22789 * @cfg {Boolean} singleSelect
22790 * True to allow selection of only one row at a time (defaults to false)
22792 singleSelect : false,
22795 initEvents : function()
22798 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22799 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
22800 //}else{ // allow click to work like normal
22801 // this.grid.on("rowclick", this.handleDragableRowClick, this);
22803 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22804 this.grid.on("rowclick", this.handleMouseDown, this);
22806 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22807 "up" : function(e){
22809 this.selectPrevious(e.shiftKey);
22810 }else if(this.last !== false && this.lastActive !== false){
22811 var last = this.last;
22812 this.selectRange(this.last, this.lastActive-1);
22813 this.grid.getView().focusRow(this.lastActive);
22814 if(last !== false){
22818 this.selectFirstRow();
22820 this.fireEvent("afterselectionchange", this);
22822 "down" : function(e){
22824 this.selectNext(e.shiftKey);
22825 }else if(this.last !== false && this.lastActive !== false){
22826 var last = this.last;
22827 this.selectRange(this.last, this.lastActive+1);
22828 this.grid.getView().focusRow(this.lastActive);
22829 if(last !== false){
22833 this.selectFirstRow();
22835 this.fireEvent("afterselectionchange", this);
22839 this.grid.store.on('load', function(){
22840 this.selections.clear();
22843 var view = this.grid.view;
22844 view.on("refresh", this.onRefresh, this);
22845 view.on("rowupdated", this.onRowUpdated, this);
22846 view.on("rowremoved", this.onRemove, this);
22851 onRefresh : function()
22853 var ds = this.grid.store, i, v = this.grid.view;
22854 var s = this.selections;
22855 s.each(function(r){
22856 if((i = ds.indexOfId(r.id)) != -1){
22865 onRemove : function(v, index, r){
22866 this.selections.remove(r);
22870 onRowUpdated : function(v, index, r){
22871 if(this.isSelected(r)){
22872 v.onRowSelect(index);
22878 * @param {Array} records The records to select
22879 * @param {Boolean} keepExisting (optional) True to keep existing selections
22881 selectRecords : function(records, keepExisting)
22884 this.clearSelections();
22886 var ds = this.grid.store;
22887 for(var i = 0, len = records.length; i < len; i++){
22888 this.selectRow(ds.indexOf(records[i]), true);
22893 * Gets the number of selected rows.
22896 getCount : function(){
22897 return this.selections.length;
22901 * Selects the first row in the grid.
22903 selectFirstRow : function(){
22908 * Select the last row.
22909 * @param {Boolean} keepExisting (optional) True to keep existing selections
22911 selectLastRow : function(keepExisting){
22912 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22913 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22917 * Selects the row immediately following the last selected row.
22918 * @param {Boolean} keepExisting (optional) True to keep existing selections
22920 selectNext : function(keepExisting)
22922 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22923 this.selectRow(this.last+1, keepExisting);
22924 this.grid.getView().focusRow(this.last);
22929 * Selects the row that precedes the last selected row.
22930 * @param {Boolean} keepExisting (optional) True to keep existing selections
22932 selectPrevious : function(keepExisting){
22934 this.selectRow(this.last-1, keepExisting);
22935 this.grid.getView().focusRow(this.last);
22940 * Returns the selected records
22941 * @return {Array} Array of selected records
22943 getSelections : function(){
22944 return [].concat(this.selections.items);
22948 * Returns the first selected record.
22951 getSelected : function(){
22952 return this.selections.itemAt(0);
22957 * Clears all selections.
22959 clearSelections : function(fast)
22965 var ds = this.grid.store;
22966 var s = this.selections;
22967 s.each(function(r){
22968 this.deselectRow(ds.indexOfId(r.id));
22972 this.selections.clear();
22979 * Selects all rows.
22981 selectAll : function(){
22985 this.selections.clear();
22986 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
22987 this.selectRow(i, true);
22992 * Returns True if there is a selection.
22993 * @return {Boolean}
22995 hasSelection : function(){
22996 return this.selections.length > 0;
23000 * Returns True if the specified row is selected.
23001 * @param {Number/Record} record The record or index of the record to check
23002 * @return {Boolean}
23004 isSelected : function(index){
23005 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23006 return (r && this.selections.key(r.id) ? true : false);
23010 * Returns True if the specified record id is selected.
23011 * @param {String} id The id of record to check
23012 * @return {Boolean}
23014 isIdSelected : function(id){
23015 return (this.selections.key(id) ? true : false);
23020 handleMouseDBClick : function(e, t){
23024 handleMouseDown : function(e, t)
23026 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23027 if(this.isLocked() || rowIndex < 0 ){
23030 if(e.shiftKey && this.last !== false){
23031 var last = this.last;
23032 this.selectRange(last, rowIndex, e.ctrlKey);
23033 this.last = last; // reset the last
23037 var isSelected = this.isSelected(rowIndex);
23038 //Roo.log("select row:" + rowIndex);
23040 this.deselectRow(rowIndex);
23042 this.selectRow(rowIndex, true);
23046 if(e.button !== 0 && isSelected){
23047 alert('rowIndex 2: ' + rowIndex);
23048 view.focusRow(rowIndex);
23049 }else if(e.ctrlKey && isSelected){
23050 this.deselectRow(rowIndex);
23051 }else if(!isSelected){
23052 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23053 view.focusRow(rowIndex);
23057 this.fireEvent("afterselectionchange", this);
23060 handleDragableRowClick : function(grid, rowIndex, e)
23062 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23063 this.selectRow(rowIndex, false);
23064 grid.view.focusRow(rowIndex);
23065 this.fireEvent("afterselectionchange", this);
23070 * Selects multiple rows.
23071 * @param {Array} rows Array of the indexes of the row to select
23072 * @param {Boolean} keepExisting (optional) True to keep existing selections
23074 selectRows : function(rows, keepExisting){
23076 this.clearSelections();
23078 for(var i = 0, len = rows.length; i < len; i++){
23079 this.selectRow(rows[i], true);
23084 * Selects a range of rows. All rows in between startRow and endRow are also selected.
23085 * @param {Number} startRow The index of the first row in the range
23086 * @param {Number} endRow The index of the last row in the range
23087 * @param {Boolean} keepExisting (optional) True to retain existing selections
23089 selectRange : function(startRow, endRow, keepExisting){
23094 this.clearSelections();
23096 if(startRow <= endRow){
23097 for(var i = startRow; i <= endRow; i++){
23098 this.selectRow(i, true);
23101 for(var i = startRow; i >= endRow; i--){
23102 this.selectRow(i, true);
23108 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23109 * @param {Number} startRow The index of the first row in the range
23110 * @param {Number} endRow The index of the last row in the range
23112 deselectRange : function(startRow, endRow, preventViewNotify){
23116 for(var i = startRow; i <= endRow; i++){
23117 this.deselectRow(i, preventViewNotify);
23123 * @param {Number} row The index of the row to select
23124 * @param {Boolean} keepExisting (optional) True to keep existing selections
23126 selectRow : function(index, keepExisting, preventViewNotify)
23128 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23131 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23132 if(!keepExisting || this.singleSelect){
23133 this.clearSelections();
23136 var r = this.grid.store.getAt(index);
23137 //console.log('selectRow - record id :' + r.id);
23139 this.selections.add(r);
23140 this.last = this.lastActive = index;
23141 if(!preventViewNotify){
23142 var proxy = new Roo.Element(
23143 this.grid.getRowDom(index)
23145 proxy.addClass('bg-info info');
23147 this.fireEvent("rowselect", this, index, r);
23148 this.fireEvent("selectionchange", this);
23154 * @param {Number} row The index of the row to deselect
23156 deselectRow : function(index, preventViewNotify)
23161 if(this.last == index){
23164 if(this.lastActive == index){
23165 this.lastActive = false;
23168 var r = this.grid.store.getAt(index);
23173 this.selections.remove(r);
23174 //.console.log('deselectRow - record id :' + r.id);
23175 if(!preventViewNotify){
23177 var proxy = new Roo.Element(
23178 this.grid.getRowDom(index)
23180 proxy.removeClass('bg-info info');
23182 this.fireEvent("rowdeselect", this, index);
23183 this.fireEvent("selectionchange", this);
23187 restoreLast : function(){
23189 this.last = this._last;
23194 acceptsNav : function(row, col, cm){
23195 return !cm.isHidden(col) && cm.isCellEditable(col, row);
23199 onEditorKey : function(field, e){
23200 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23205 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23207 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23209 }else if(k == e.ENTER && !e.ctrlKey){
23213 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23215 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23217 }else if(k == e.ESC){
23221 g.startEditing(newCell[0], newCell[1]);
23227 * Ext JS Library 1.1.1
23228 * Copyright(c) 2006-2007, Ext JS, LLC.
23230 * Originally Released Under LGPL - original licence link has changed is not relivant.
23233 * <script type="text/javascript">
23237 * @class Roo.bootstrap.PagingToolbar
23238 * @extends Roo.bootstrap.NavSimplebar
23239 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23241 * Create a new PagingToolbar
23242 * @param {Object} config The config object
23243 * @param {Roo.data.Store} store
23245 Roo.bootstrap.PagingToolbar = function(config)
23247 // old args format still supported... - xtype is prefered..
23248 // created from xtype...
23250 this.ds = config.dataSource;
23252 if (config.store && !this.ds) {
23253 this.store= Roo.factory(config.store, Roo.data);
23254 this.ds = this.store;
23255 this.ds.xmodule = this.xmodule || false;
23258 this.toolbarItems = [];
23259 if (config.items) {
23260 this.toolbarItems = config.items;
23263 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23268 this.bind(this.ds);
23271 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23275 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23277 * @cfg {Roo.data.Store} dataSource
23278 * The underlying data store providing the paged data
23281 * @cfg {String/HTMLElement/Element} container
23282 * container The id or element that will contain the toolbar
23285 * @cfg {Boolean} displayInfo
23286 * True to display the displayMsg (defaults to false)
23289 * @cfg {Number} pageSize
23290 * The number of records to display per page (defaults to 20)
23294 * @cfg {String} displayMsg
23295 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23297 displayMsg : 'Displaying {0} - {1} of {2}',
23299 * @cfg {String} emptyMsg
23300 * The message to display when no records are found (defaults to "No data to display")
23302 emptyMsg : 'No data to display',
23304 * Customizable piece of the default paging text (defaults to "Page")
23307 beforePageText : "Page",
23309 * Customizable piece of the default paging text (defaults to "of %0")
23312 afterPageText : "of {0}",
23314 * Customizable piece of the default paging text (defaults to "First Page")
23317 firstText : "First Page",
23319 * Customizable piece of the default paging text (defaults to "Previous Page")
23322 prevText : "Previous Page",
23324 * Customizable piece of the default paging text (defaults to "Next Page")
23327 nextText : "Next Page",
23329 * Customizable piece of the default paging text (defaults to "Last Page")
23332 lastText : "Last Page",
23334 * Customizable piece of the default paging text (defaults to "Refresh")
23337 refreshText : "Refresh",
23341 onRender : function(ct, position)
23343 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23344 this.navgroup.parentId = this.id;
23345 this.navgroup.onRender(this.el, null);
23346 // add the buttons to the navgroup
23348 if(this.displayInfo){
23349 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23350 this.displayEl = this.el.select('.x-paging-info', true).first();
23351 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23352 // this.displayEl = navel.el.select('span',true).first();
23358 Roo.each(_this.buttons, function(e){ // this might need to use render????
23359 Roo.factory(e).onRender(_this.el, null);
23363 Roo.each(_this.toolbarItems, function(e) {
23364 _this.navgroup.addItem(e);
23368 this.first = this.navgroup.addItem({
23369 tooltip: this.firstText,
23371 icon : 'fa fa-backward',
23373 preventDefault: true,
23374 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23377 this.prev = this.navgroup.addItem({
23378 tooltip: this.prevText,
23380 icon : 'fa fa-step-backward',
23382 preventDefault: true,
23383 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
23385 //this.addSeparator();
23388 var field = this.navgroup.addItem( {
23390 cls : 'x-paging-position',
23392 html : this.beforePageText +
23393 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23394 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
23397 this.field = field.el.select('input', true).first();
23398 this.field.on("keydown", this.onPagingKeydown, this);
23399 this.field.on("focus", function(){this.dom.select();});
23402 this.afterTextEl = field.el.select('.x-paging-after',true).first();
23403 //this.field.setHeight(18);
23404 //this.addSeparator();
23405 this.next = this.navgroup.addItem({
23406 tooltip: this.nextText,
23408 html : ' <i class="fa fa-step-forward">',
23410 preventDefault: true,
23411 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
23413 this.last = this.navgroup.addItem({
23414 tooltip: this.lastText,
23415 icon : 'fa fa-forward',
23418 preventDefault: true,
23419 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
23421 //this.addSeparator();
23422 this.loading = this.navgroup.addItem({
23423 tooltip: this.refreshText,
23424 icon: 'fa fa-refresh',
23425 preventDefault: true,
23426 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23432 updateInfo : function(){
23433 if(this.displayEl){
23434 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23435 var msg = count == 0 ?
23439 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
23441 this.displayEl.update(msg);
23446 onLoad : function(ds, r, o){
23447 this.cursor = o.params ? o.params.start : 0;
23448 var d = this.getPageData(),
23452 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23453 this.field.dom.value = ap;
23454 this.first.setDisabled(ap == 1);
23455 this.prev.setDisabled(ap == 1);
23456 this.next.setDisabled(ap == ps);
23457 this.last.setDisabled(ap == ps);
23458 this.loading.enable();
23463 getPageData : function(){
23464 var total = this.ds.getTotalCount();
23467 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23468 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23473 onLoadError : function(){
23474 this.loading.enable();
23478 onPagingKeydown : function(e){
23479 var k = e.getKey();
23480 var d = this.getPageData();
23482 var v = this.field.dom.value, pageNum;
23483 if(!v || isNaN(pageNum = parseInt(v, 10))){
23484 this.field.dom.value = d.activePage;
23487 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23488 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23491 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))
23493 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23494 this.field.dom.value = pageNum;
23495 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23498 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23500 var v = this.field.dom.value, pageNum;
23501 var increment = (e.shiftKey) ? 10 : 1;
23502 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23505 if(!v || isNaN(pageNum = parseInt(v, 10))) {
23506 this.field.dom.value = d.activePage;
23509 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23511 this.field.dom.value = parseInt(v, 10) + increment;
23512 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23513 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23520 beforeLoad : function(){
23522 this.loading.disable();
23527 onClick : function(which){
23536 ds.load({params:{start: 0, limit: this.pageSize}});
23539 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23542 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23545 var total = ds.getTotalCount();
23546 var extra = total % this.pageSize;
23547 var lastStart = extra ? (total - extra) : total-this.pageSize;
23548 ds.load({params:{start: lastStart, limit: this.pageSize}});
23551 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23557 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23558 * @param {Roo.data.Store} store The data store to unbind
23560 unbind : function(ds){
23561 ds.un("beforeload", this.beforeLoad, this);
23562 ds.un("load", this.onLoad, this);
23563 ds.un("loadexception", this.onLoadError, this);
23564 ds.un("remove", this.updateInfo, this);
23565 ds.un("add", this.updateInfo, this);
23566 this.ds = undefined;
23570 * Binds the paging toolbar to the specified {@link Roo.data.Store}
23571 * @param {Roo.data.Store} store The data store to bind
23573 bind : function(ds){
23574 ds.on("beforeload", this.beforeLoad, this);
23575 ds.on("load", this.onLoad, this);
23576 ds.on("loadexception", this.onLoadError, this);
23577 ds.on("remove", this.updateInfo, this);
23578 ds.on("add", this.updateInfo, this);
23589 * @class Roo.bootstrap.MessageBar
23590 * @extends Roo.bootstrap.Component
23591 * Bootstrap MessageBar class
23592 * @cfg {String} html contents of the MessageBar
23593 * @cfg {String} weight (info | success | warning | danger) default info
23594 * @cfg {String} beforeClass insert the bar before the given class
23595 * @cfg {Boolean} closable (true | false) default false
23596 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23599 * Create a new Element
23600 * @param {Object} config The config object
23603 Roo.bootstrap.MessageBar = function(config){
23604 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23607 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
23613 beforeClass: 'bootstrap-sticky-wrap',
23615 getAutoCreate : function(){
23619 cls: 'alert alert-dismissable alert-' + this.weight,
23624 html: this.html || ''
23630 cfg.cls += ' alert-messages-fixed';
23644 onRender : function(ct, position)
23646 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23649 var cfg = Roo.apply({}, this.getAutoCreate());
23653 cfg.cls += ' ' + this.cls;
23656 cfg.style = this.style;
23658 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23660 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23663 this.el.select('>button.close').on('click', this.hide, this);
23669 if (!this.rendered) {
23675 this.fireEvent('show', this);
23681 if (!this.rendered) {
23687 this.fireEvent('hide', this);
23690 update : function()
23692 // var e = this.el.dom.firstChild;
23694 // if(this.closable){
23695 // e = e.nextSibling;
23698 // e.data = this.html || '';
23700 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23716 * @class Roo.bootstrap.Graph
23717 * @extends Roo.bootstrap.Component
23718 * Bootstrap Graph class
23722 @cfg {String} graphtype bar | vbar | pie
23723 @cfg {number} g_x coodinator | centre x (pie)
23724 @cfg {number} g_y coodinator | centre y (pie)
23725 @cfg {number} g_r radius (pie)
23726 @cfg {number} g_height height of the chart (respected by all elements in the set)
23727 @cfg {number} g_width width of the chart (respected by all elements in the set)
23728 @cfg {Object} title The title of the chart
23731 -opts (object) options for the chart
23733 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23734 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23736 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.
23737 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23739 o stretch (boolean)
23741 -opts (object) options for the pie
23744 o startAngle (number)
23745 o endAngle (number)
23749 * Create a new Input
23750 * @param {Object} config The config object
23753 Roo.bootstrap.Graph = function(config){
23754 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23760 * The img click event for the img.
23761 * @param {Roo.EventObject} e
23767 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23778 //g_colors: this.colors,
23785 getAutoCreate : function(){
23796 onRender : function(ct,position){
23799 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23801 if (typeof(Raphael) == 'undefined') {
23802 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23806 this.raphael = Raphael(this.el.dom);
23808 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23809 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23810 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23811 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23813 r.text(160, 10, "Single Series Chart").attr(txtattr);
23814 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23815 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23816 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23818 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23819 r.barchart(330, 10, 300, 220, data1);
23820 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23821 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23824 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23825 // r.barchart(30, 30, 560, 250, xdata, {
23826 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23827 // axis : "0 0 1 1",
23828 // axisxlabels : xdata
23829 // //yvalues : cols,
23832 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23834 // this.load(null,xdata,{
23835 // axis : "0 0 1 1",
23836 // axisxlabels : xdata
23841 load : function(graphtype,xdata,opts)
23843 this.raphael.clear();
23845 graphtype = this.graphtype;
23850 var r = this.raphael,
23851 fin = function () {
23852 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23854 fout = function () {
23855 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23857 pfin = function() {
23858 this.sector.stop();
23859 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23862 this.label[0].stop();
23863 this.label[0].attr({ r: 7.5 });
23864 this.label[1].attr({ "font-weight": 800 });
23867 pfout = function() {
23868 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23871 this.label[0].animate({ r: 5 }, 500, "bounce");
23872 this.label[1].attr({ "font-weight": 400 });
23878 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23881 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23884 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23885 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23887 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23894 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23899 setTitle: function(o)
23904 initEvents: function() {
23907 this.el.on('click', this.onClick, this);
23911 onClick : function(e)
23913 Roo.log('img onclick');
23914 this.fireEvent('click', this, e);
23926 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23929 * @class Roo.bootstrap.dash.NumberBox
23930 * @extends Roo.bootstrap.Component
23931 * Bootstrap NumberBox class
23932 * @cfg {String} headline Box headline
23933 * @cfg {String} content Box content
23934 * @cfg {String} icon Box icon
23935 * @cfg {String} footer Footer text
23936 * @cfg {String} fhref Footer href
23939 * Create a new NumberBox
23940 * @param {Object} config The config object
23944 Roo.bootstrap.dash.NumberBox = function(config){
23945 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23949 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23958 getAutoCreate : function(){
23962 cls : 'small-box ',
23970 cls : 'roo-headline',
23971 html : this.headline
23975 cls : 'roo-content',
23976 html : this.content
23990 cls : 'ion ' + this.icon
23999 cls : 'small-box-footer',
24000 href : this.fhref || '#',
24004 cfg.cn.push(footer);
24011 onRender : function(ct,position){
24012 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24019 setHeadline: function (value)
24021 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24024 setFooter: function (value, href)
24026 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24029 this.el.select('a.small-box-footer',true).first().attr('href', href);
24034 setContent: function (value)
24036 this.el.select('.roo-content',true).first().dom.innerHTML = value;
24039 initEvents: function()
24053 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24056 * @class Roo.bootstrap.dash.TabBox
24057 * @extends Roo.bootstrap.Component
24058 * Bootstrap TabBox class
24059 * @cfg {String} title Title of the TabBox
24060 * @cfg {String} icon Icon of the TabBox
24061 * @cfg {Boolean} showtabs (true|false) show the tabs default true
24062 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24065 * Create a new TabBox
24066 * @param {Object} config The config object
24070 Roo.bootstrap.dash.TabBox = function(config){
24071 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24076 * When a pane is added
24077 * @param {Roo.bootstrap.dash.TabPane} pane
24081 * @event activatepane
24082 * When a pane is activated
24083 * @param {Roo.bootstrap.dash.TabPane} pane
24085 "activatepane" : true
24093 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
24098 tabScrollable : false,
24100 getChildContainer : function()
24102 return this.el.select('.tab-content', true).first();
24105 getAutoCreate : function(){
24109 cls: 'pull-left header',
24117 cls: 'fa ' + this.icon
24123 cls: 'nav nav-tabs pull-right',
24129 if(this.tabScrollable){
24136 cls: 'nav nav-tabs pull-right',
24147 cls: 'nav-tabs-custom',
24152 cls: 'tab-content no-padding',
24160 initEvents : function()
24162 //Roo.log('add add pane handler');
24163 this.on('addpane', this.onAddPane, this);
24166 * Updates the box title
24167 * @param {String} html to set the title to.
24169 setTitle : function(value)
24171 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24173 onAddPane : function(pane)
24175 this.panes.push(pane);
24176 //Roo.log('addpane');
24178 // tabs are rendere left to right..
24179 if(!this.showtabs){
24183 var ctr = this.el.select('.nav-tabs', true).first();
24186 var existing = ctr.select('.nav-tab',true);
24187 var qty = existing.getCount();;
24190 var tab = ctr.createChild({
24192 cls : 'nav-tab' + (qty ? '' : ' active'),
24200 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24203 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24205 pane.el.addClass('active');
24210 onTabClick : function(ev,un,ob,pane)
24212 //Roo.log('tab - prev default');
24213 ev.preventDefault();
24216 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24217 pane.tab.addClass('active');
24218 //Roo.log(pane.title);
24219 this.getChildContainer().select('.tab-pane',true).removeClass('active');
24220 // technically we should have a deactivate event.. but maybe add later.
24221 // and it should not de-activate the selected tab...
24222 this.fireEvent('activatepane', pane);
24223 pane.el.addClass('active');
24224 pane.fireEvent('activate');
24229 getActivePane : function()
24232 Roo.each(this.panes, function(p) {
24233 if(p.el.hasClass('active')){
24254 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24256 * @class Roo.bootstrap.TabPane
24257 * @extends Roo.bootstrap.Component
24258 * Bootstrap TabPane class
24259 * @cfg {Boolean} active (false | true) Default false
24260 * @cfg {String} title title of panel
24264 * Create a new TabPane
24265 * @param {Object} config The config object
24268 Roo.bootstrap.dash.TabPane = function(config){
24269 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24275 * When a pane is activated
24276 * @param {Roo.bootstrap.dash.TabPane} pane
24283 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
24288 // the tabBox that this is attached to.
24291 getAutoCreate : function()
24299 cfg.cls += ' active';
24304 initEvents : function()
24306 //Roo.log('trigger add pane handler');
24307 this.parent().fireEvent('addpane', this)
24311 * Updates the tab title
24312 * @param {String} html to set the title to.
24314 setTitle: function(str)
24320 this.tab.select('a', true).first().dom.innerHTML = str;
24337 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24340 * @class Roo.bootstrap.menu.Menu
24341 * @extends Roo.bootstrap.Component
24342 * Bootstrap Menu class - container for Menu
24343 * @cfg {String} html Text of the menu
24344 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24345 * @cfg {String} icon Font awesome icon
24346 * @cfg {String} pos Menu align to (top | bottom) default bottom
24350 * Create a new Menu
24351 * @param {Object} config The config object
24355 Roo.bootstrap.menu.Menu = function(config){
24356 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24360 * @event beforeshow
24361 * Fires before this menu is displayed
24362 * @param {Roo.bootstrap.menu.Menu} this
24366 * @event beforehide
24367 * Fires before this menu is hidden
24368 * @param {Roo.bootstrap.menu.Menu} this
24373 * Fires after this menu is displayed
24374 * @param {Roo.bootstrap.menu.Menu} this
24379 * Fires after this menu is hidden
24380 * @param {Roo.bootstrap.menu.Menu} this
24385 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24386 * @param {Roo.bootstrap.menu.Menu} this
24387 * @param {Roo.EventObject} e
24394 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
24398 weight : 'default',
24403 getChildContainer : function() {
24404 if(this.isSubMenu){
24408 return this.el.select('ul.dropdown-menu', true).first();
24411 getAutoCreate : function()
24416 cls : 'roo-menu-text',
24424 cls : 'fa ' + this.icon
24435 cls : 'dropdown-button btn btn-' + this.weight,
24440 cls : 'dropdown-toggle btn btn-' + this.weight,
24450 cls : 'dropdown-menu'
24456 if(this.pos == 'top'){
24457 cfg.cls += ' dropup';
24460 if(this.isSubMenu){
24463 cls : 'dropdown-menu'
24470 onRender : function(ct, position)
24472 this.isSubMenu = ct.hasClass('dropdown-submenu');
24474 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24477 initEvents : function()
24479 if(this.isSubMenu){
24483 this.hidden = true;
24485 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24486 this.triggerEl.on('click', this.onTriggerPress, this);
24488 this.buttonEl = this.el.select('button.dropdown-button', true).first();
24489 this.buttonEl.on('click', this.onClick, this);
24495 if(this.isSubMenu){
24499 return this.el.select('ul.dropdown-menu', true).first();
24502 onClick : function(e)
24504 this.fireEvent("click", this, e);
24507 onTriggerPress : function(e)
24509 if (this.isVisible()) {
24516 isVisible : function(){
24517 return !this.hidden;
24522 this.fireEvent("beforeshow", this);
24524 this.hidden = false;
24525 this.el.addClass('open');
24527 Roo.get(document).on("mouseup", this.onMouseUp, this);
24529 this.fireEvent("show", this);
24536 this.fireEvent("beforehide", this);
24538 this.hidden = true;
24539 this.el.removeClass('open');
24541 Roo.get(document).un("mouseup", this.onMouseUp);
24543 this.fireEvent("hide", this);
24546 onMouseUp : function()
24560 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24563 * @class Roo.bootstrap.menu.Item
24564 * @extends Roo.bootstrap.Component
24565 * Bootstrap MenuItem class
24566 * @cfg {Boolean} submenu (true | false) default false
24567 * @cfg {String} html text of the item
24568 * @cfg {String} href the link
24569 * @cfg {Boolean} disable (true | false) default false
24570 * @cfg {Boolean} preventDefault (true | false) default true
24571 * @cfg {String} icon Font awesome icon
24572 * @cfg {String} pos Submenu align to (left | right) default right
24576 * Create a new Item
24577 * @param {Object} config The config object
24581 Roo.bootstrap.menu.Item = function(config){
24582 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24586 * Fires when the mouse is hovering over this menu
24587 * @param {Roo.bootstrap.menu.Item} this
24588 * @param {Roo.EventObject} e
24593 * Fires when the mouse exits this menu
24594 * @param {Roo.bootstrap.menu.Item} this
24595 * @param {Roo.EventObject} e
24601 * The raw click event for the entire grid.
24602 * @param {Roo.EventObject} e
24608 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
24613 preventDefault: true,
24618 getAutoCreate : function()
24623 cls : 'roo-menu-item-text',
24631 cls : 'fa ' + this.icon
24640 href : this.href || '#',
24647 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24651 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24653 if(this.pos == 'left'){
24654 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24661 initEvents : function()
24663 this.el.on('mouseover', this.onMouseOver, this);
24664 this.el.on('mouseout', this.onMouseOut, this);
24666 this.el.select('a', true).first().on('click', this.onClick, this);
24670 onClick : function(e)
24672 if(this.preventDefault){
24673 e.preventDefault();
24676 this.fireEvent("click", this, e);
24679 onMouseOver : function(e)
24681 if(this.submenu && this.pos == 'left'){
24682 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24685 this.fireEvent("mouseover", this, e);
24688 onMouseOut : function(e)
24690 this.fireEvent("mouseout", this, e);
24702 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24705 * @class Roo.bootstrap.menu.Separator
24706 * @extends Roo.bootstrap.Component
24707 * Bootstrap Separator class
24710 * Create a new Separator
24711 * @param {Object} config The config object
24715 Roo.bootstrap.menu.Separator = function(config){
24716 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24719 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24721 getAutoCreate : function(){
24742 * @class Roo.bootstrap.Tooltip
24743 * Bootstrap Tooltip class
24744 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24745 * to determine which dom element triggers the tooltip.
24747 * It needs to add support for additional attributes like tooltip-position
24750 * Create a new Toolti
24751 * @param {Object} config The config object
24754 Roo.bootstrap.Tooltip = function(config){
24755 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24758 Roo.apply(Roo.bootstrap.Tooltip, {
24760 * @function init initialize tooltip monitoring.
24764 currentTip : false,
24765 currentRegion : false,
24771 Roo.get(document).on('mouseover', this.enter ,this);
24772 Roo.get(document).on('mouseout', this.leave, this);
24775 this.currentTip = new Roo.bootstrap.Tooltip();
24778 enter : function(ev)
24780 var dom = ev.getTarget();
24782 //Roo.log(['enter',dom]);
24783 var el = Roo.fly(dom);
24784 if (this.currentEl) {
24786 //Roo.log(this.currentEl);
24787 //Roo.log(this.currentEl.contains(dom));
24788 if (this.currentEl == el) {
24791 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24797 if (this.currentTip.el) {
24798 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24802 if(!el || el.dom == document){
24808 // you can not look for children, as if el is the body.. then everythign is the child..
24809 if (!el.attr('tooltip')) { //
24810 if (!el.select("[tooltip]").elements.length) {
24813 // is the mouse over this child...?
24814 bindEl = el.select("[tooltip]").first();
24815 var xy = ev.getXY();
24816 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24817 //Roo.log("not in region.");
24820 //Roo.log("child element over..");
24823 this.currentEl = bindEl;
24824 this.currentTip.bind(bindEl);
24825 this.currentRegion = Roo.lib.Region.getRegion(dom);
24826 this.currentTip.enter();
24829 leave : function(ev)
24831 var dom = ev.getTarget();
24832 //Roo.log(['leave',dom]);
24833 if (!this.currentEl) {
24838 if (dom != this.currentEl.dom) {
24841 var xy = ev.getXY();
24842 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24845 // only activate leave if mouse cursor is outside... bounding box..
24850 if (this.currentTip) {
24851 this.currentTip.leave();
24853 //Roo.log('clear currentEl');
24854 this.currentEl = false;
24859 'left' : ['r-l', [-2,0], 'right'],
24860 'right' : ['l-r', [2,0], 'left'],
24861 'bottom' : ['t-b', [0,2], 'top'],
24862 'top' : [ 'b-t', [0,-2], 'bottom']
24868 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24873 delay : null, // can be { show : 300 , hide: 500}
24877 hoverState : null, //???
24879 placement : 'bottom',
24881 getAutoCreate : function(){
24888 cls : 'tooltip-arrow'
24891 cls : 'tooltip-inner'
24898 bind : function(el)
24904 enter : function () {
24906 if (this.timeout != null) {
24907 clearTimeout(this.timeout);
24910 this.hoverState = 'in';
24911 //Roo.log("enter - show");
24912 if (!this.delay || !this.delay.show) {
24917 this.timeout = setTimeout(function () {
24918 if (_t.hoverState == 'in') {
24921 }, this.delay.show);
24925 clearTimeout(this.timeout);
24927 this.hoverState = 'out';
24928 if (!this.delay || !this.delay.hide) {
24934 this.timeout = setTimeout(function () {
24935 //Roo.log("leave - timeout");
24937 if (_t.hoverState == 'out') {
24939 Roo.bootstrap.Tooltip.currentEl = false;
24947 this.render(document.body);
24950 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24952 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24954 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24956 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24958 var placement = typeof this.placement == 'function' ?
24959 this.placement.call(this, this.el, on_el) :
24962 var autoToken = /\s?auto?\s?/i;
24963 var autoPlace = autoToken.test(placement);
24965 placement = placement.replace(autoToken, '') || 'top';
24969 //this.el.setXY([0,0]);
24971 //this.el.dom.style.display='block';
24973 //this.el.appendTo(on_el);
24975 var p = this.getPosition();
24976 var box = this.el.getBox();
24982 var align = Roo.bootstrap.Tooltip.alignment[placement];
24984 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24986 if(placement == 'top' || placement == 'bottom'){
24988 placement = 'right';
24991 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24992 placement = 'left';
24995 var scroll = Roo.select('body', true).first().getScroll();
24997 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25003 align = Roo.bootstrap.Tooltip.alignment[placement];
25005 this.el.alignTo(this.bindEl, align[0],align[1]);
25006 //var arrow = this.el.select('.arrow',true).first();
25007 //arrow.set(align[2],
25009 this.el.addClass(placement);
25011 this.el.addClass('in fade');
25013 this.hoverState = null;
25015 if (this.el.hasClass('fade')) {
25026 //this.el.setXY([0,0]);
25027 this.el.removeClass('in');
25043 * @class Roo.bootstrap.LocationPicker
25044 * @extends Roo.bootstrap.Component
25045 * Bootstrap LocationPicker class
25046 * @cfg {Number} latitude Position when init default 0
25047 * @cfg {Number} longitude Position when init default 0
25048 * @cfg {Number} zoom default 15
25049 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25050 * @cfg {Boolean} mapTypeControl default false
25051 * @cfg {Boolean} disableDoubleClickZoom default false
25052 * @cfg {Boolean} scrollwheel default true
25053 * @cfg {Boolean} streetViewControl default false
25054 * @cfg {Number} radius default 0
25055 * @cfg {String} locationName
25056 * @cfg {Boolean} draggable default true
25057 * @cfg {Boolean} enableAutocomplete default false
25058 * @cfg {Boolean} enableReverseGeocode default true
25059 * @cfg {String} markerTitle
25062 * Create a new LocationPicker
25063 * @param {Object} config The config object
25067 Roo.bootstrap.LocationPicker = function(config){
25069 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25074 * Fires when the picker initialized.
25075 * @param {Roo.bootstrap.LocationPicker} this
25076 * @param {Google Location} location
25080 * @event positionchanged
25081 * Fires when the picker position changed.
25082 * @param {Roo.bootstrap.LocationPicker} this
25083 * @param {Google Location} location
25085 positionchanged : true,
25088 * Fires when the map resize.
25089 * @param {Roo.bootstrap.LocationPicker} this
25094 * Fires when the map show.
25095 * @param {Roo.bootstrap.LocationPicker} this
25100 * Fires when the map hide.
25101 * @param {Roo.bootstrap.LocationPicker} this
25106 * Fires when click the map.
25107 * @param {Roo.bootstrap.LocationPicker} this
25108 * @param {Map event} e
25112 * @event mapRightClick
25113 * Fires when right click the map.
25114 * @param {Roo.bootstrap.LocationPicker} this
25115 * @param {Map event} e
25117 mapRightClick : true,
25119 * @event markerClick
25120 * Fires when click the marker.
25121 * @param {Roo.bootstrap.LocationPicker} this
25122 * @param {Map event} e
25124 markerClick : true,
25126 * @event markerRightClick
25127 * Fires when right click the marker.
25128 * @param {Roo.bootstrap.LocationPicker} this
25129 * @param {Map event} e
25131 markerRightClick : true,
25133 * @event OverlayViewDraw
25134 * Fires when OverlayView Draw
25135 * @param {Roo.bootstrap.LocationPicker} this
25137 OverlayViewDraw : true,
25139 * @event OverlayViewOnAdd
25140 * Fires when OverlayView Draw
25141 * @param {Roo.bootstrap.LocationPicker} this
25143 OverlayViewOnAdd : true,
25145 * @event OverlayViewOnRemove
25146 * Fires when OverlayView Draw
25147 * @param {Roo.bootstrap.LocationPicker} this
25149 OverlayViewOnRemove : true,
25151 * @event OverlayViewShow
25152 * Fires when OverlayView Draw
25153 * @param {Roo.bootstrap.LocationPicker} this
25154 * @param {Pixel} cpx
25156 OverlayViewShow : true,
25158 * @event OverlayViewHide
25159 * Fires when OverlayView Draw
25160 * @param {Roo.bootstrap.LocationPicker} this
25162 OverlayViewHide : true,
25164 * @event loadexception
25165 * Fires when load google lib failed.
25166 * @param {Roo.bootstrap.LocationPicker} this
25168 loadexception : true
25173 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
25175 gMapContext: false,
25181 mapTypeControl: false,
25182 disableDoubleClickZoom: false,
25184 streetViewControl: false,
25188 enableAutocomplete: false,
25189 enableReverseGeocode: true,
25192 getAutoCreate: function()
25197 cls: 'roo-location-picker'
25203 initEvents: function(ct, position)
25205 if(!this.el.getWidth() || this.isApplied()){
25209 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25214 initial: function()
25216 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25217 this.fireEvent('loadexception', this);
25221 if(!this.mapTypeId){
25222 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25225 this.gMapContext = this.GMapContext();
25227 this.initOverlayView();
25229 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25233 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25234 _this.setPosition(_this.gMapContext.marker.position);
25237 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25238 _this.fireEvent('mapClick', this, event);
25242 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25243 _this.fireEvent('mapRightClick', this, event);
25247 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25248 _this.fireEvent('markerClick', this, event);
25252 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25253 _this.fireEvent('markerRightClick', this, event);
25257 this.setPosition(this.gMapContext.location);
25259 this.fireEvent('initial', this, this.gMapContext.location);
25262 initOverlayView: function()
25266 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25270 _this.fireEvent('OverlayViewDraw', _this);
25275 _this.fireEvent('OverlayViewOnAdd', _this);
25278 onRemove: function()
25280 _this.fireEvent('OverlayViewOnRemove', _this);
25283 show: function(cpx)
25285 _this.fireEvent('OverlayViewShow', _this, cpx);
25290 _this.fireEvent('OverlayViewHide', _this);
25296 fromLatLngToContainerPixel: function(event)
25298 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25301 isApplied: function()
25303 return this.getGmapContext() == false ? false : true;
25306 getGmapContext: function()
25308 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25311 GMapContext: function()
25313 var position = new google.maps.LatLng(this.latitude, this.longitude);
25315 var _map = new google.maps.Map(this.el.dom, {
25318 mapTypeId: this.mapTypeId,
25319 mapTypeControl: this.mapTypeControl,
25320 disableDoubleClickZoom: this.disableDoubleClickZoom,
25321 scrollwheel: this.scrollwheel,
25322 streetViewControl: this.streetViewControl,
25323 locationName: this.locationName,
25324 draggable: this.draggable,
25325 enableAutocomplete: this.enableAutocomplete,
25326 enableReverseGeocode: this.enableReverseGeocode
25329 var _marker = new google.maps.Marker({
25330 position: position,
25332 title: this.markerTitle,
25333 draggable: this.draggable
25340 location: position,
25341 radius: this.radius,
25342 locationName: this.locationName,
25343 addressComponents: {
25344 formatted_address: null,
25345 addressLine1: null,
25346 addressLine2: null,
25348 streetNumber: null,
25352 stateOrProvince: null
25355 domContainer: this.el.dom,
25356 geodecoder: new google.maps.Geocoder()
25360 drawCircle: function(center, radius, options)
25362 if (this.gMapContext.circle != null) {
25363 this.gMapContext.circle.setMap(null);
25367 options = Roo.apply({}, options, {
25368 strokeColor: "#0000FF",
25369 strokeOpacity: .35,
25371 fillColor: "#0000FF",
25375 options.map = this.gMapContext.map;
25376 options.radius = radius;
25377 options.center = center;
25378 this.gMapContext.circle = new google.maps.Circle(options);
25379 return this.gMapContext.circle;
25385 setPosition: function(location)
25387 this.gMapContext.location = location;
25388 this.gMapContext.marker.setPosition(location);
25389 this.gMapContext.map.panTo(location);
25390 this.drawCircle(location, this.gMapContext.radius, {});
25394 if (this.gMapContext.settings.enableReverseGeocode) {
25395 this.gMapContext.geodecoder.geocode({
25396 latLng: this.gMapContext.location
25397 }, function(results, status) {
25399 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25400 _this.gMapContext.locationName = results[0].formatted_address;
25401 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25403 _this.fireEvent('positionchanged', this, location);
25410 this.fireEvent('positionchanged', this, location);
25415 google.maps.event.trigger(this.gMapContext.map, "resize");
25417 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25419 this.fireEvent('resize', this);
25422 setPositionByLatLng: function(latitude, longitude)
25424 this.setPosition(new google.maps.LatLng(latitude, longitude));
25427 getCurrentPosition: function()
25430 latitude: this.gMapContext.location.lat(),
25431 longitude: this.gMapContext.location.lng()
25435 getAddressName: function()
25437 return this.gMapContext.locationName;
25440 getAddressComponents: function()
25442 return this.gMapContext.addressComponents;
25445 address_component_from_google_geocode: function(address_components)
25449 for (var i = 0; i < address_components.length; i++) {
25450 var component = address_components[i];
25451 if (component.types.indexOf("postal_code") >= 0) {
25452 result.postalCode = component.short_name;
25453 } else if (component.types.indexOf("street_number") >= 0) {
25454 result.streetNumber = component.short_name;
25455 } else if (component.types.indexOf("route") >= 0) {
25456 result.streetName = component.short_name;
25457 } else if (component.types.indexOf("neighborhood") >= 0) {
25458 result.city = component.short_name;
25459 } else if (component.types.indexOf("locality") >= 0) {
25460 result.city = component.short_name;
25461 } else if (component.types.indexOf("sublocality") >= 0) {
25462 result.district = component.short_name;
25463 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25464 result.stateOrProvince = component.short_name;
25465 } else if (component.types.indexOf("country") >= 0) {
25466 result.country = component.short_name;
25470 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25471 result.addressLine2 = "";
25475 setZoomLevel: function(zoom)
25477 this.gMapContext.map.setZoom(zoom);
25490 this.fireEvent('show', this);
25501 this.fireEvent('hide', this);
25506 Roo.apply(Roo.bootstrap.LocationPicker, {
25508 OverlayView : function(map, options)
25510 options = options || {};
25524 * @class Roo.bootstrap.Alert
25525 * @extends Roo.bootstrap.Component
25526 * Bootstrap Alert class
25527 * @cfg {String} title The title of alert
25528 * @cfg {String} html The content of alert
25529 * @cfg {String} weight ( success | info | warning | danger )
25530 * @cfg {String} faicon font-awesomeicon
25533 * Create a new alert
25534 * @param {Object} config The config object
25538 Roo.bootstrap.Alert = function(config){
25539 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25543 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
25550 getAutoCreate : function()
25559 cls : 'roo-alert-icon'
25564 cls : 'roo-alert-title',
25569 cls : 'roo-alert-text',
25576 cfg.cn[0].cls += ' fa ' + this.faicon;
25580 cfg.cls += ' alert-' + this.weight;
25586 initEvents: function()
25588 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25591 setTitle : function(str)
25593 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25596 setText : function(str)
25598 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25601 setWeight : function(weight)
25604 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25607 this.weight = weight;
25609 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25612 setIcon : function(icon)
25615 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25618 this.faicon = icon;
25620 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25641 * @class Roo.bootstrap.UploadCropbox
25642 * @extends Roo.bootstrap.Component
25643 * Bootstrap UploadCropbox class
25644 * @cfg {String} emptyText show when image has been loaded
25645 * @cfg {String} rotateNotify show when image too small to rotate
25646 * @cfg {Number} errorTimeout default 3000
25647 * @cfg {Number} minWidth default 300
25648 * @cfg {Number} minHeight default 300
25649 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25650 * @cfg {Boolean} isDocument (true|false) default false
25651 * @cfg {String} url action url
25652 * @cfg {String} paramName default 'imageUpload'
25653 * @cfg {String} method default POST
25654 * @cfg {Boolean} loadMask (true|false) default true
25655 * @cfg {Boolean} loadingText default 'Loading...'
25658 * Create a new UploadCropbox
25659 * @param {Object} config The config object
25662 Roo.bootstrap.UploadCropbox = function(config){
25663 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25667 * @event beforeselectfile
25668 * Fire before select file
25669 * @param {Roo.bootstrap.UploadCropbox} this
25671 "beforeselectfile" : true,
25674 * Fire after initEvent
25675 * @param {Roo.bootstrap.UploadCropbox} this
25680 * Fire after initEvent
25681 * @param {Roo.bootstrap.UploadCropbox} this
25682 * @param {String} data
25687 * Fire when preparing the file data
25688 * @param {Roo.bootstrap.UploadCropbox} this
25689 * @param {Object} file
25694 * Fire when get exception
25695 * @param {Roo.bootstrap.UploadCropbox} this
25696 * @param {XMLHttpRequest} xhr
25698 "exception" : true,
25700 * @event beforeloadcanvas
25701 * Fire before load the canvas
25702 * @param {Roo.bootstrap.UploadCropbox} this
25703 * @param {String} src
25705 "beforeloadcanvas" : true,
25708 * Fire when trash image
25709 * @param {Roo.bootstrap.UploadCropbox} this
25714 * Fire when download the image
25715 * @param {Roo.bootstrap.UploadCropbox} this
25719 * @event footerbuttonclick
25720 * Fire when footerbuttonclick
25721 * @param {Roo.bootstrap.UploadCropbox} this
25722 * @param {String} type
25724 "footerbuttonclick" : true,
25728 * @param {Roo.bootstrap.UploadCropbox} this
25733 * Fire when rotate the image
25734 * @param {Roo.bootstrap.UploadCropbox} this
25735 * @param {String} pos
25740 * Fire when inspect the file
25741 * @param {Roo.bootstrap.UploadCropbox} this
25742 * @param {Object} file
25747 * Fire when xhr upload the file
25748 * @param {Roo.bootstrap.UploadCropbox} this
25749 * @param {Object} data
25754 * Fire when arrange the file data
25755 * @param {Roo.bootstrap.UploadCropbox} this
25756 * @param {Object} formData
25761 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25764 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25766 emptyText : 'Click to upload image',
25767 rotateNotify : 'Image is too small to rotate',
25768 errorTimeout : 3000,
25782 cropType : 'image/jpeg',
25784 canvasLoaded : false,
25785 isDocument : false,
25787 paramName : 'imageUpload',
25789 loadingText : 'Loading...',
25792 getAutoCreate : function()
25796 cls : 'roo-upload-cropbox',
25800 cls : 'roo-upload-cropbox-selector',
25805 cls : 'roo-upload-cropbox-body',
25806 style : 'cursor:pointer',
25810 cls : 'roo-upload-cropbox-preview'
25814 cls : 'roo-upload-cropbox-thumb'
25818 cls : 'roo-upload-cropbox-empty-notify',
25819 html : this.emptyText
25823 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25824 html : this.rotateNotify
25830 cls : 'roo-upload-cropbox-footer',
25833 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25843 onRender : function(ct, position)
25845 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25847 if (this.buttons.length) {
25849 Roo.each(this.buttons, function(bb) {
25851 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25853 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25859 this.maskEl = this.el;
25863 initEvents : function()
25865 this.urlAPI = (window.createObjectURL && window) ||
25866 (window.URL && URL.revokeObjectURL && URL) ||
25867 (window.webkitURL && webkitURL);
25869 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25870 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25872 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25873 this.selectorEl.hide();
25875 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25876 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25878 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25879 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25880 this.thumbEl.hide();
25882 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25883 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25885 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25886 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25887 this.errorEl.hide();
25889 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25890 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25891 this.footerEl.hide();
25893 this.setThumbBoxSize();
25899 this.fireEvent('initial', this);
25906 window.addEventListener("resize", function() { _this.resize(); } );
25908 this.bodyEl.on('click', this.beforeSelectFile, this);
25911 this.bodyEl.on('touchstart', this.onTouchStart, this);
25912 this.bodyEl.on('touchmove', this.onTouchMove, this);
25913 this.bodyEl.on('touchend', this.onTouchEnd, this);
25917 this.bodyEl.on('mousedown', this.onMouseDown, this);
25918 this.bodyEl.on('mousemove', this.onMouseMove, this);
25919 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25920 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25921 Roo.get(document).on('mouseup', this.onMouseUp, this);
25924 this.selectorEl.on('change', this.onFileSelected, this);
25930 this.baseScale = 1;
25932 this.baseRotate = 1;
25933 this.dragable = false;
25934 this.pinching = false;
25937 this.cropData = false;
25938 this.notifyEl.dom.innerHTML = this.emptyText;
25940 this.selectorEl.dom.value = '';
25944 resize : function()
25946 if(this.fireEvent('resize', this) != false){
25947 this.setThumbBoxPosition();
25948 this.setCanvasPosition();
25952 onFooterButtonClick : function(e, el, o, type)
25955 case 'rotate-left' :
25956 this.onRotateLeft(e);
25958 case 'rotate-right' :
25959 this.onRotateRight(e);
25962 this.beforeSelectFile(e);
25977 this.fireEvent('footerbuttonclick', this, type);
25980 beforeSelectFile : function(e)
25982 e.preventDefault();
25984 if(this.fireEvent('beforeselectfile', this) != false){
25985 this.selectorEl.dom.click();
25989 onFileSelected : function(e)
25991 e.preventDefault();
25993 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25997 var file = this.selectorEl.dom.files[0];
25999 if(this.fireEvent('inspect', this, file) != false){
26000 this.prepare(file);
26005 trash : function(e)
26007 this.fireEvent('trash', this);
26010 download : function(e)
26012 this.fireEvent('download', this);
26015 loadCanvas : function(src)
26017 if(this.fireEvent('beforeloadcanvas', this, src) != false){
26021 this.imageEl = document.createElement('img');
26025 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26027 this.imageEl.src = src;
26031 onLoadCanvas : function()
26033 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26034 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26036 this.bodyEl.un('click', this.beforeSelectFile, this);
26038 this.notifyEl.hide();
26039 this.thumbEl.show();
26040 this.footerEl.show();
26042 this.baseRotateLevel();
26044 if(this.isDocument){
26045 this.setThumbBoxSize();
26048 this.setThumbBoxPosition();
26050 this.baseScaleLevel();
26056 this.canvasLoaded = true;
26059 this.maskEl.unmask();
26064 setCanvasPosition : function()
26066 if(!this.canvasEl){
26070 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26071 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26073 this.previewEl.setLeft(pw);
26074 this.previewEl.setTop(ph);
26078 onMouseDown : function(e)
26082 this.dragable = true;
26083 this.pinching = false;
26085 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26086 this.dragable = false;
26090 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26091 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26095 onMouseMove : function(e)
26099 if(!this.canvasLoaded){
26103 if (!this.dragable){
26107 var minX = Math.ceil(this.thumbEl.getLeft(true));
26108 var minY = Math.ceil(this.thumbEl.getTop(true));
26110 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26111 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26113 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26114 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26116 x = x - this.mouseX;
26117 y = y - this.mouseY;
26119 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26120 var bgY = Math.ceil(y + this.previewEl.getTop(true));
26122 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26123 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26125 this.previewEl.setLeft(bgX);
26126 this.previewEl.setTop(bgY);
26128 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26129 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26132 onMouseUp : function(e)
26136 this.dragable = false;
26139 onMouseWheel : function(e)
26143 this.startScale = this.scale;
26145 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26147 if(!this.zoomable()){
26148 this.scale = this.startScale;
26157 zoomable : function()
26159 var minScale = this.thumbEl.getWidth() / this.minWidth;
26161 if(this.minWidth < this.minHeight){
26162 minScale = this.thumbEl.getHeight() / this.minHeight;
26165 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26166 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26170 (this.rotate == 0 || this.rotate == 180) &&
26172 width > this.imageEl.OriginWidth ||
26173 height > this.imageEl.OriginHeight ||
26174 (width < this.minWidth && height < this.minHeight)
26182 (this.rotate == 90 || this.rotate == 270) &&
26184 width > this.imageEl.OriginWidth ||
26185 height > this.imageEl.OriginHeight ||
26186 (width < this.minHeight && height < this.minWidth)
26193 !this.isDocument &&
26194 (this.rotate == 0 || this.rotate == 180) &&
26196 width < this.minWidth ||
26197 width > this.imageEl.OriginWidth ||
26198 height < this.minHeight ||
26199 height > this.imageEl.OriginHeight
26206 !this.isDocument &&
26207 (this.rotate == 90 || this.rotate == 270) &&
26209 width < this.minHeight ||
26210 width > this.imageEl.OriginWidth ||
26211 height < this.minWidth ||
26212 height > this.imageEl.OriginHeight
26222 onRotateLeft : function(e)
26224 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26226 var minScale = this.thumbEl.getWidth() / this.minWidth;
26228 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26229 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26231 this.startScale = this.scale;
26233 while (this.getScaleLevel() < minScale){
26235 this.scale = this.scale + 1;
26237 if(!this.zoomable()){
26242 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26243 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26248 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26255 this.scale = this.startScale;
26257 this.onRotateFail();
26262 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26264 if(this.isDocument){
26265 this.setThumbBoxSize();
26266 this.setThumbBoxPosition();
26267 this.setCanvasPosition();
26272 this.fireEvent('rotate', this, 'left');
26276 onRotateRight : function(e)
26278 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26280 var minScale = this.thumbEl.getWidth() / this.minWidth;
26282 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26283 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26285 this.startScale = this.scale;
26287 while (this.getScaleLevel() < minScale){
26289 this.scale = this.scale + 1;
26291 if(!this.zoomable()){
26296 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26297 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26302 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26309 this.scale = this.startScale;
26311 this.onRotateFail();
26316 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26318 if(this.isDocument){
26319 this.setThumbBoxSize();
26320 this.setThumbBoxPosition();
26321 this.setCanvasPosition();
26326 this.fireEvent('rotate', this, 'right');
26329 onRotateFail : function()
26331 this.errorEl.show(true);
26335 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26340 this.previewEl.dom.innerHTML = '';
26342 var canvasEl = document.createElement("canvas");
26344 var contextEl = canvasEl.getContext("2d");
26346 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26347 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26348 var center = this.imageEl.OriginWidth / 2;
26350 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26351 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26352 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26353 center = this.imageEl.OriginHeight / 2;
26356 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26358 contextEl.translate(center, center);
26359 contextEl.rotate(this.rotate * Math.PI / 180);
26361 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26363 this.canvasEl = document.createElement("canvas");
26365 this.contextEl = this.canvasEl.getContext("2d");
26367 switch (this.rotate) {
26370 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26371 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26373 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26378 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26379 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26381 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26382 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);
26386 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26391 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26392 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26394 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26395 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);
26399 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);
26404 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26405 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26407 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26408 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26412 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26419 this.previewEl.appendChild(this.canvasEl);
26421 this.setCanvasPosition();
26426 if(!this.canvasLoaded){
26430 var imageCanvas = document.createElement("canvas");
26432 var imageContext = imageCanvas.getContext("2d");
26434 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26435 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26437 var center = imageCanvas.width / 2;
26439 imageContext.translate(center, center);
26441 imageContext.rotate(this.rotate * Math.PI / 180);
26443 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26445 var canvas = document.createElement("canvas");
26447 var context = canvas.getContext("2d");
26449 canvas.width = this.minWidth;
26450 canvas.height = this.minHeight;
26452 switch (this.rotate) {
26455 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26456 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26458 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26459 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26461 var targetWidth = this.minWidth - 2 * x;
26462 var targetHeight = this.minHeight - 2 * y;
26466 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26467 scale = targetWidth / width;
26470 if(x > 0 && y == 0){
26471 scale = targetHeight / height;
26474 if(x > 0 && y > 0){
26475 scale = targetWidth / width;
26477 if(width < height){
26478 scale = targetHeight / height;
26482 context.scale(scale, scale);
26484 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26485 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26487 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26488 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26490 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26495 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26496 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26498 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26499 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26501 var targetWidth = this.minWidth - 2 * x;
26502 var targetHeight = this.minHeight - 2 * y;
26506 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26507 scale = targetWidth / width;
26510 if(x > 0 && y == 0){
26511 scale = targetHeight / height;
26514 if(x > 0 && y > 0){
26515 scale = targetWidth / width;
26517 if(width < height){
26518 scale = targetHeight / height;
26522 context.scale(scale, scale);
26524 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26525 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26527 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26528 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26530 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26532 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26537 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26538 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26540 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26541 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26543 var targetWidth = this.minWidth - 2 * x;
26544 var targetHeight = this.minHeight - 2 * y;
26548 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26549 scale = targetWidth / width;
26552 if(x > 0 && y == 0){
26553 scale = targetHeight / height;
26556 if(x > 0 && y > 0){
26557 scale = targetWidth / width;
26559 if(width < height){
26560 scale = targetHeight / height;
26564 context.scale(scale, scale);
26566 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26567 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26569 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26570 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26572 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26573 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26575 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26580 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26581 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26583 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26584 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26586 var targetWidth = this.minWidth - 2 * x;
26587 var targetHeight = this.minHeight - 2 * y;
26591 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26592 scale = targetWidth / width;
26595 if(x > 0 && y == 0){
26596 scale = targetHeight / height;
26599 if(x > 0 && y > 0){
26600 scale = targetWidth / width;
26602 if(width < height){
26603 scale = targetHeight / height;
26607 context.scale(scale, scale);
26609 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26610 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26612 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26613 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26615 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26617 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26624 this.cropData = canvas.toDataURL(this.cropType);
26626 if(this.fireEvent('crop', this, this.cropData) !== false){
26627 this.process(this.file, this.cropData);
26634 setThumbBoxSize : function()
26638 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26639 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26640 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26642 this.minWidth = width;
26643 this.minHeight = height;
26645 if(this.rotate == 90 || this.rotate == 270){
26646 this.minWidth = height;
26647 this.minHeight = width;
26652 width = Math.ceil(this.minWidth * height / this.minHeight);
26654 if(this.minWidth > this.minHeight){
26656 height = Math.ceil(this.minHeight * width / this.minWidth);
26659 this.thumbEl.setStyle({
26660 width : width + 'px',
26661 height : height + 'px'
26668 setThumbBoxPosition : function()
26670 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26671 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26673 this.thumbEl.setLeft(x);
26674 this.thumbEl.setTop(y);
26678 baseRotateLevel : function()
26680 this.baseRotate = 1;
26683 typeof(this.exif) != 'undefined' &&
26684 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26685 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26687 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26690 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26694 baseScaleLevel : function()
26698 if(this.isDocument){
26700 if(this.baseRotate == 6 || this.baseRotate == 8){
26702 height = this.thumbEl.getHeight();
26703 this.baseScale = height / this.imageEl.OriginWidth;
26705 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26706 width = this.thumbEl.getWidth();
26707 this.baseScale = width / this.imageEl.OriginHeight;
26713 height = this.thumbEl.getHeight();
26714 this.baseScale = height / this.imageEl.OriginHeight;
26716 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26717 width = this.thumbEl.getWidth();
26718 this.baseScale = width / this.imageEl.OriginWidth;
26724 if(this.baseRotate == 6 || this.baseRotate == 8){
26726 width = this.thumbEl.getHeight();
26727 this.baseScale = width / this.imageEl.OriginHeight;
26729 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26730 height = this.thumbEl.getWidth();
26731 this.baseScale = height / this.imageEl.OriginHeight;
26734 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26735 height = this.thumbEl.getWidth();
26736 this.baseScale = height / this.imageEl.OriginHeight;
26738 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26739 width = this.thumbEl.getHeight();
26740 this.baseScale = width / this.imageEl.OriginWidth;
26747 width = this.thumbEl.getWidth();
26748 this.baseScale = width / this.imageEl.OriginWidth;
26750 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26751 height = this.thumbEl.getHeight();
26752 this.baseScale = height / this.imageEl.OriginHeight;
26755 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26757 height = this.thumbEl.getHeight();
26758 this.baseScale = height / this.imageEl.OriginHeight;
26760 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26761 width = this.thumbEl.getWidth();
26762 this.baseScale = width / this.imageEl.OriginWidth;
26770 getScaleLevel : function()
26772 return this.baseScale * Math.pow(1.1, this.scale);
26775 onTouchStart : function(e)
26777 if(!this.canvasLoaded){
26778 this.beforeSelectFile(e);
26782 var touches = e.browserEvent.touches;
26788 if(touches.length == 1){
26789 this.onMouseDown(e);
26793 if(touches.length != 2){
26799 for(var i = 0, finger; finger = touches[i]; i++){
26800 coords.push(finger.pageX, finger.pageY);
26803 var x = Math.pow(coords[0] - coords[2], 2);
26804 var y = Math.pow(coords[1] - coords[3], 2);
26806 this.startDistance = Math.sqrt(x + y);
26808 this.startScale = this.scale;
26810 this.pinching = true;
26811 this.dragable = false;
26815 onTouchMove : function(e)
26817 if(!this.pinching && !this.dragable){
26821 var touches = e.browserEvent.touches;
26828 this.onMouseMove(e);
26834 for(var i = 0, finger; finger = touches[i]; i++){
26835 coords.push(finger.pageX, finger.pageY);
26838 var x = Math.pow(coords[0] - coords[2], 2);
26839 var y = Math.pow(coords[1] - coords[3], 2);
26841 this.endDistance = Math.sqrt(x + y);
26843 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26845 if(!this.zoomable()){
26846 this.scale = this.startScale;
26854 onTouchEnd : function(e)
26856 this.pinching = false;
26857 this.dragable = false;
26861 process : function(file, crop)
26864 this.maskEl.mask(this.loadingText);
26867 this.xhr = new XMLHttpRequest();
26869 file.xhr = this.xhr;
26871 this.xhr.open(this.method, this.url, true);
26874 "Accept": "application/json",
26875 "Cache-Control": "no-cache",
26876 "X-Requested-With": "XMLHttpRequest"
26879 for (var headerName in headers) {
26880 var headerValue = headers[headerName];
26882 this.xhr.setRequestHeader(headerName, headerValue);
26888 this.xhr.onload = function()
26890 _this.xhrOnLoad(_this.xhr);
26893 this.xhr.onerror = function()
26895 _this.xhrOnError(_this.xhr);
26898 var formData = new FormData();
26900 formData.append('returnHTML', 'NO');
26903 formData.append('crop', crop);
26906 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26907 formData.append(this.paramName, file, file.name);
26910 if(typeof(file.filename) != 'undefined'){
26911 formData.append('filename', file.filename);
26914 if(typeof(file.mimetype) != 'undefined'){
26915 formData.append('mimetype', file.mimetype);
26918 if(this.fireEvent('arrange', this, formData) != false){
26919 this.xhr.send(formData);
26923 xhrOnLoad : function(xhr)
26926 this.maskEl.unmask();
26929 if (xhr.readyState !== 4) {
26930 this.fireEvent('exception', this, xhr);
26934 var response = Roo.decode(xhr.responseText);
26936 if(!response.success){
26937 this.fireEvent('exception', this, xhr);
26941 var response = Roo.decode(xhr.responseText);
26943 this.fireEvent('upload', this, response);
26947 xhrOnError : function()
26950 this.maskEl.unmask();
26953 Roo.log('xhr on error');
26955 var response = Roo.decode(xhr.responseText);
26961 prepare : function(file)
26964 this.maskEl.mask(this.loadingText);
26970 if(typeof(file) === 'string'){
26971 this.loadCanvas(file);
26975 if(!file || !this.urlAPI){
26980 this.cropType = file.type;
26984 if(this.fireEvent('prepare', this, this.file) != false){
26986 var reader = new FileReader();
26988 reader.onload = function (e) {
26989 if (e.target.error) {
26990 Roo.log(e.target.error);
26994 var buffer = e.target.result,
26995 dataView = new DataView(buffer),
26997 maxOffset = dataView.byteLength - 4,
27001 if (dataView.getUint16(0) === 0xffd8) {
27002 while (offset < maxOffset) {
27003 markerBytes = dataView.getUint16(offset);
27005 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27006 markerLength = dataView.getUint16(offset + 2) + 2;
27007 if (offset + markerLength > dataView.byteLength) {
27008 Roo.log('Invalid meta data: Invalid segment size.');
27012 if(markerBytes == 0xffe1){
27013 _this.parseExifData(
27020 offset += markerLength;
27030 var url = _this.urlAPI.createObjectURL(_this.file);
27032 _this.loadCanvas(url);
27037 reader.readAsArrayBuffer(this.file);
27043 parseExifData : function(dataView, offset, length)
27045 var tiffOffset = offset + 10,
27049 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27050 // No Exif data, might be XMP data instead
27054 // Check for the ASCII code for "Exif" (0x45786966):
27055 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27056 // No Exif data, might be XMP data instead
27059 if (tiffOffset + 8 > dataView.byteLength) {
27060 Roo.log('Invalid Exif data: Invalid segment size.');
27063 // Check for the two null bytes:
27064 if (dataView.getUint16(offset + 8) !== 0x0000) {
27065 Roo.log('Invalid Exif data: Missing byte alignment offset.');
27068 // Check the byte alignment:
27069 switch (dataView.getUint16(tiffOffset)) {
27071 littleEndian = true;
27074 littleEndian = false;
27077 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27080 // Check for the TIFF tag marker (0x002A):
27081 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27082 Roo.log('Invalid Exif data: Missing TIFF marker.');
27085 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27086 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27088 this.parseExifTags(
27091 tiffOffset + dirOffset,
27096 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27101 if (dirOffset + 6 > dataView.byteLength) {
27102 Roo.log('Invalid Exif data: Invalid directory offset.');
27105 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27106 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27107 if (dirEndOffset + 4 > dataView.byteLength) {
27108 Roo.log('Invalid Exif data: Invalid directory size.');
27111 for (i = 0; i < tagsNumber; i += 1) {
27115 dirOffset + 2 + 12 * i, // tag offset
27119 // Return the offset to the next directory:
27120 return dataView.getUint32(dirEndOffset, littleEndian);
27123 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
27125 var tag = dataView.getUint16(offset, littleEndian);
27127 this.exif[tag] = this.getExifValue(
27131 dataView.getUint16(offset + 2, littleEndian), // tag type
27132 dataView.getUint32(offset + 4, littleEndian), // tag length
27137 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27139 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27148 Roo.log('Invalid Exif data: Invalid tag type.');
27152 tagSize = tagType.size * length;
27153 // Determine if the value is contained in the dataOffset bytes,
27154 // or if the value at the dataOffset is a pointer to the actual data:
27155 dataOffset = tagSize > 4 ?
27156 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27157 if (dataOffset + tagSize > dataView.byteLength) {
27158 Roo.log('Invalid Exif data: Invalid data offset.');
27161 if (length === 1) {
27162 return tagType.getValue(dataView, dataOffset, littleEndian);
27165 for (i = 0; i < length; i += 1) {
27166 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27169 if (tagType.ascii) {
27171 // Concatenate the chars:
27172 for (i = 0; i < values.length; i += 1) {
27174 // Ignore the terminating NULL byte(s):
27175 if (c === '\u0000') {
27187 Roo.apply(Roo.bootstrap.UploadCropbox, {
27189 'Orientation': 0x0112
27193 1: 0, //'top-left',
27195 3: 180, //'bottom-right',
27196 // 4: 'bottom-left',
27198 6: 90, //'right-top',
27199 // 7: 'right-bottom',
27200 8: 270 //'left-bottom'
27204 // byte, 8-bit unsigned int:
27206 getValue: function (dataView, dataOffset) {
27207 return dataView.getUint8(dataOffset);
27211 // ascii, 8-bit byte:
27213 getValue: function (dataView, dataOffset) {
27214 return String.fromCharCode(dataView.getUint8(dataOffset));
27219 // short, 16 bit int:
27221 getValue: function (dataView, dataOffset, littleEndian) {
27222 return dataView.getUint16(dataOffset, littleEndian);
27226 // long, 32 bit int:
27228 getValue: function (dataView, dataOffset, littleEndian) {
27229 return dataView.getUint32(dataOffset, littleEndian);
27233 // rational = two long values, first is numerator, second is denominator:
27235 getValue: function (dataView, dataOffset, littleEndian) {
27236 return dataView.getUint32(dataOffset, littleEndian) /
27237 dataView.getUint32(dataOffset + 4, littleEndian);
27241 // slong, 32 bit signed int:
27243 getValue: function (dataView, dataOffset, littleEndian) {
27244 return dataView.getInt32(dataOffset, littleEndian);
27248 // srational, two slongs, first is numerator, second is denominator:
27250 getValue: function (dataView, dataOffset, littleEndian) {
27251 return dataView.getInt32(dataOffset, littleEndian) /
27252 dataView.getInt32(dataOffset + 4, littleEndian);
27262 cls : 'btn-group roo-upload-cropbox-rotate-left',
27263 action : 'rotate-left',
27267 cls : 'btn btn-default',
27268 html : '<i class="fa fa-undo"></i>'
27274 cls : 'btn-group roo-upload-cropbox-picture',
27275 action : 'picture',
27279 cls : 'btn btn-default',
27280 html : '<i class="fa fa-picture-o"></i>'
27286 cls : 'btn-group roo-upload-cropbox-rotate-right',
27287 action : 'rotate-right',
27291 cls : 'btn btn-default',
27292 html : '<i class="fa fa-repeat"></i>'
27300 cls : 'btn-group roo-upload-cropbox-rotate-left',
27301 action : 'rotate-left',
27305 cls : 'btn btn-default',
27306 html : '<i class="fa fa-undo"></i>'
27312 cls : 'btn-group roo-upload-cropbox-download',
27313 action : 'download',
27317 cls : 'btn btn-default',
27318 html : '<i class="fa fa-download"></i>'
27324 cls : 'btn-group roo-upload-cropbox-crop',
27329 cls : 'btn btn-default',
27330 html : '<i class="fa fa-crop"></i>'
27336 cls : 'btn-group roo-upload-cropbox-trash',
27341 cls : 'btn btn-default',
27342 html : '<i class="fa fa-trash"></i>'
27348 cls : 'btn-group roo-upload-cropbox-rotate-right',
27349 action : 'rotate-right',
27353 cls : 'btn btn-default',
27354 html : '<i class="fa fa-repeat"></i>'
27362 cls : 'btn-group roo-upload-cropbox-rotate-left',
27363 action : 'rotate-left',
27367 cls : 'btn btn-default',
27368 html : '<i class="fa fa-undo"></i>'
27374 cls : 'btn-group roo-upload-cropbox-rotate-right',
27375 action : 'rotate-right',
27379 cls : 'btn btn-default',
27380 html : '<i class="fa fa-repeat"></i>'
27393 * @class Roo.bootstrap.DocumentManager
27394 * @extends Roo.bootstrap.Component
27395 * Bootstrap DocumentManager class
27396 * @cfg {String} paramName default 'imageUpload'
27397 * @cfg {String} method default POST
27398 * @cfg {String} url action url
27399 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27400 * @cfg {Boolean} multiple multiple upload default true
27401 * @cfg {Number} thumbSize default 300
27402 * @cfg {String} fieldLabel
27403 * @cfg {Number} labelWidth default 4
27404 * @cfg {String} labelAlign (left|top) default left
27405 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27408 * Create a new DocumentManager
27409 * @param {Object} config The config object
27412 Roo.bootstrap.DocumentManager = function(config){
27413 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27416 this.delegates = [];
27421 * Fire when initial the DocumentManager
27422 * @param {Roo.bootstrap.DocumentManager} this
27427 * inspect selected file
27428 * @param {Roo.bootstrap.DocumentManager} this
27429 * @param {File} file
27434 * Fire when xhr load exception
27435 * @param {Roo.bootstrap.DocumentManager} this
27436 * @param {XMLHttpRequest} xhr
27438 "exception" : true,
27440 * @event afterupload
27441 * Fire when xhr load exception
27442 * @param {Roo.bootstrap.DocumentManager} this
27443 * @param {XMLHttpRequest} xhr
27445 "afterupload" : true,
27448 * prepare the form data
27449 * @param {Roo.bootstrap.DocumentManager} this
27450 * @param {Object} formData
27455 * Fire when remove the file
27456 * @param {Roo.bootstrap.DocumentManager} this
27457 * @param {Object} file
27462 * Fire after refresh the file
27463 * @param {Roo.bootstrap.DocumentManager} this
27468 * Fire after click the image
27469 * @param {Roo.bootstrap.DocumentManager} this
27470 * @param {Object} file
27475 * Fire when upload a image and editable set to true
27476 * @param {Roo.bootstrap.DocumentManager} this
27477 * @param {Object} file
27481 * @event beforeselectfile
27482 * Fire before select file
27483 * @param {Roo.bootstrap.DocumentManager} this
27485 "beforeselectfile" : true,
27488 * Fire before process file
27489 * @param {Roo.bootstrap.DocumentManager} this
27490 * @param {Object} file
27497 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
27506 paramName : 'imageUpload',
27509 labelAlign : 'left',
27516 getAutoCreate : function()
27518 var managerWidget = {
27520 cls : 'roo-document-manager',
27524 cls : 'roo-document-manager-selector',
27529 cls : 'roo-document-manager-uploader',
27533 cls : 'roo-document-manager-upload-btn',
27534 html : '<i class="fa fa-plus"></i>'
27545 cls : 'column col-md-12',
27550 if(this.fieldLabel.length){
27555 cls : 'column col-md-12',
27556 html : this.fieldLabel
27560 cls : 'column col-md-12',
27565 if(this.labelAlign == 'left'){
27569 cls : 'column col-md-' + this.labelWidth,
27570 html : this.fieldLabel
27574 cls : 'column col-md-' + (12 - this.labelWidth),
27584 cls : 'row clearfix',
27592 initEvents : function()
27594 this.managerEl = this.el.select('.roo-document-manager', true).first();
27595 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27597 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27598 this.selectorEl.hide();
27601 this.selectorEl.attr('multiple', 'multiple');
27604 this.selectorEl.on('change', this.onFileSelected, this);
27606 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27607 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27609 this.uploader.on('click', this.onUploaderClick, this);
27611 this.renderProgressDialog();
27615 window.addEventListener("resize", function() { _this.refresh(); } );
27617 this.fireEvent('initial', this);
27620 renderProgressDialog : function()
27624 this.progressDialog = new Roo.bootstrap.Modal({
27625 cls : 'roo-document-manager-progress-dialog',
27626 allow_close : false,
27636 btnclick : function() {
27637 _this.uploadCancel();
27643 this.progressDialog.render(Roo.get(document.body));
27645 this.progress = new Roo.bootstrap.Progress({
27646 cls : 'roo-document-manager-progress',
27651 this.progress.render(this.progressDialog.getChildContainer());
27653 this.progressBar = new Roo.bootstrap.ProgressBar({
27654 cls : 'roo-document-manager-progress-bar',
27657 aria_valuemax : 12,
27661 this.progressBar.render(this.progress.getChildContainer());
27664 onUploaderClick : function(e)
27666 e.preventDefault();
27668 if(this.fireEvent('beforeselectfile', this) != false){
27669 this.selectorEl.dom.click();
27674 onFileSelected : function(e)
27676 e.preventDefault();
27678 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27682 Roo.each(this.selectorEl.dom.files, function(file){
27683 if(this.fireEvent('inspect', this, file) != false){
27684 this.files.push(file);
27694 this.selectorEl.dom.value = '';
27696 if(!this.files.length){
27700 if(this.boxes > 0 && this.files.length > this.boxes){
27701 this.files = this.files.slice(0, this.boxes);
27704 this.uploader.show();
27706 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27707 this.uploader.hide();
27716 Roo.each(this.files, function(file){
27718 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27719 var f = this.renderPreview(file);
27724 if(file.type.indexOf('image') != -1){
27725 this.delegates.push(
27727 _this.process(file);
27728 }).createDelegate(this)
27736 _this.process(file);
27737 }).createDelegate(this)
27742 this.files = files;
27744 this.delegates = this.delegates.concat(docs);
27746 if(!this.delegates.length){
27751 this.progressBar.aria_valuemax = this.delegates.length;
27758 arrange : function()
27760 if(!this.delegates.length){
27761 this.progressDialog.hide();
27766 var delegate = this.delegates.shift();
27768 this.progressDialog.show();
27770 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27772 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27777 refresh : function()
27779 this.uploader.show();
27781 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27782 this.uploader.hide();
27785 Roo.isTouch ? this.closable(false) : this.closable(true);
27787 this.fireEvent('refresh', this);
27790 onRemove : function(e, el, o)
27792 e.preventDefault();
27794 this.fireEvent('remove', this, o);
27798 remove : function(o)
27802 Roo.each(this.files, function(file){
27803 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27812 this.files = files;
27819 Roo.each(this.files, function(file){
27824 file.target.remove();
27833 onClick : function(e, el, o)
27835 e.preventDefault();
27837 this.fireEvent('click', this, o);
27841 closable : function(closable)
27843 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27845 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27857 xhrOnLoad : function(xhr)
27859 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27863 if (xhr.readyState !== 4) {
27865 this.fireEvent('exception', this, xhr);
27869 var response = Roo.decode(xhr.responseText);
27871 if(!response.success){
27873 this.fireEvent('exception', this, xhr);
27877 var file = this.renderPreview(response.data);
27879 this.files.push(file);
27883 this.fireEvent('afterupload', this, xhr);
27887 xhrOnError : function(xhr)
27889 Roo.log('xhr on error');
27891 var response = Roo.decode(xhr.responseText);
27898 process : function(file)
27900 if(this.fireEvent('process', this, file) !== false){
27901 if(this.editable && file.type.indexOf('image') != -1){
27902 this.fireEvent('edit', this, file);
27906 this.uploadStart(file, false);
27913 uploadStart : function(file, crop)
27915 this.xhr = new XMLHttpRequest();
27917 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27922 file.xhr = this.xhr;
27924 this.managerEl.createChild({
27926 cls : 'roo-document-manager-loading',
27930 tooltip : file.name,
27931 cls : 'roo-document-manager-thumb',
27932 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27938 this.xhr.open(this.method, this.url, true);
27941 "Accept": "application/json",
27942 "Cache-Control": "no-cache",
27943 "X-Requested-With": "XMLHttpRequest"
27946 for (var headerName in headers) {
27947 var headerValue = headers[headerName];
27949 this.xhr.setRequestHeader(headerName, headerValue);
27955 this.xhr.onload = function()
27957 _this.xhrOnLoad(_this.xhr);
27960 this.xhr.onerror = function()
27962 _this.xhrOnError(_this.xhr);
27965 var formData = new FormData();
27967 formData.append('returnHTML', 'NO');
27970 formData.append('crop', crop);
27973 formData.append(this.paramName, file, file.name);
27975 if(this.fireEvent('prepare', this, formData) != false){
27976 this.xhr.send(formData);
27980 this.uploadCancel();
27983 uploadCancel : function()
27989 this.delegates = [];
27991 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27998 renderPreview : function(file)
28000 if(typeof(file.target) != 'undefined' && file.target){
28004 var previewEl = this.managerEl.createChild({
28006 cls : 'roo-document-manager-preview',
28010 tooltip : file.filename,
28011 cls : 'roo-document-manager-thumb',
28012 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28017 html : '<i class="fa fa-times-circle"></i>'
28022 var close = previewEl.select('button.close', true).first();
28024 close.on('click', this.onRemove, this, file);
28026 file.target = previewEl;
28028 var image = previewEl.select('img', true).first();
28032 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28034 image.on('click', this.onClick, this, file);
28040 onPreviewLoad : function(file, image)
28042 if(typeof(file.target) == 'undefined' || !file.target){
28046 var width = image.dom.naturalWidth || image.dom.width;
28047 var height = image.dom.naturalHeight || image.dom.height;
28049 if(width > height){
28050 file.target.addClass('wide');
28054 file.target.addClass('tall');
28059 uploadFromSource : function(file, crop)
28061 this.xhr = new XMLHttpRequest();
28063 this.managerEl.createChild({
28065 cls : 'roo-document-manager-loading',
28069 tooltip : file.name,
28070 cls : 'roo-document-manager-thumb',
28071 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28077 this.xhr.open(this.method, this.url, true);
28080 "Accept": "application/json",
28081 "Cache-Control": "no-cache",
28082 "X-Requested-With": "XMLHttpRequest"
28085 for (var headerName in headers) {
28086 var headerValue = headers[headerName];
28088 this.xhr.setRequestHeader(headerName, headerValue);
28094 this.xhr.onload = function()
28096 _this.xhrOnLoad(_this.xhr);
28099 this.xhr.onerror = function()
28101 _this.xhrOnError(_this.xhr);
28104 var formData = new FormData();
28106 formData.append('returnHTML', 'NO');
28108 formData.append('crop', crop);
28110 if(typeof(file.filename) != 'undefined'){
28111 formData.append('filename', file.filename);
28114 if(typeof(file.mimetype) != 'undefined'){
28115 formData.append('mimetype', file.mimetype);
28118 if(this.fireEvent('prepare', this, formData) != false){
28119 this.xhr.send(formData);
28129 * @class Roo.bootstrap.DocumentViewer
28130 * @extends Roo.bootstrap.Component
28131 * Bootstrap DocumentViewer class
28132 * @cfg {Boolean} showDownload (true|false) show download button (default true)
28133 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28136 * Create a new DocumentViewer
28137 * @param {Object} config The config object
28140 Roo.bootstrap.DocumentViewer = function(config){
28141 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28146 * Fire after initEvent
28147 * @param {Roo.bootstrap.DocumentViewer} this
28153 * @param {Roo.bootstrap.DocumentViewer} this
28158 * Fire after download button
28159 * @param {Roo.bootstrap.DocumentViewer} this
28164 * Fire after trash button
28165 * @param {Roo.bootstrap.DocumentViewer} this
28172 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
28174 showDownload : true,
28178 getAutoCreate : function()
28182 cls : 'roo-document-viewer',
28186 cls : 'roo-document-viewer-body',
28190 cls : 'roo-document-viewer-thumb',
28194 cls : 'roo-document-viewer-image'
28202 cls : 'roo-document-viewer-footer',
28205 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28209 cls : 'btn-group roo-document-viewer-download',
28213 cls : 'btn btn-default',
28214 html : '<i class="fa fa-download"></i>'
28220 cls : 'btn-group roo-document-viewer-trash',
28224 cls : 'btn btn-default',
28225 html : '<i class="fa fa-trash"></i>'
28238 initEvents : function()
28241 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28242 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28244 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28245 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28247 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28248 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28250 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28251 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28253 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28254 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28256 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28257 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28259 this.bodyEl.on('click', this.onClick, this);
28260 this.downloadBtn.on('click', this.onDownload, this);
28261 this.trashBtn.on('click', this.onTrash, this);
28263 this.downloadBtn.hide();
28264 this.trashBtn.hide();
28266 if(this.showDownload){
28267 this.downloadBtn.show();
28270 if(this.showTrash){
28271 this.trashBtn.show();
28274 if(!this.showDownload && !this.showTrash) {
28275 this.footerEl.hide();
28280 initial : function()
28282 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
28285 this.fireEvent('initial', this);
28289 onClick : function(e)
28291 e.preventDefault();
28293 this.fireEvent('click', this);
28296 onDownload : function(e)
28298 e.preventDefault();
28300 this.fireEvent('download', this);
28303 onTrash : function(e)
28305 e.preventDefault();
28307 this.fireEvent('trash', this);
28319 * @class Roo.bootstrap.NavProgressBar
28320 * @extends Roo.bootstrap.Component
28321 * Bootstrap NavProgressBar class
28324 * Create a new nav progress bar
28325 * @param {Object} config The config object
28328 Roo.bootstrap.NavProgressBar = function(config){
28329 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28331 this.bullets = this.bullets || [];
28333 // Roo.bootstrap.NavProgressBar.register(this);
28337 * Fires when the active item changes
28338 * @param {Roo.bootstrap.NavProgressBar} this
28339 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28340 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
28347 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
28352 getAutoCreate : function()
28354 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28358 cls : 'roo-navigation-bar-group',
28362 cls : 'roo-navigation-top-bar'
28366 cls : 'roo-navigation-bullets-bar',
28370 cls : 'roo-navigation-bar'
28377 cls : 'roo-navigation-bottom-bar'
28387 initEvents: function()
28392 onRender : function(ct, position)
28394 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28396 if(this.bullets.length){
28397 Roo.each(this.bullets, function(b){
28406 addItem : function(cfg)
28408 var item = new Roo.bootstrap.NavProgressItem(cfg);
28410 item.parentId = this.id;
28411 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28414 var top = new Roo.bootstrap.Element({
28416 cls : 'roo-navigation-bar-text'
28419 var bottom = new Roo.bootstrap.Element({
28421 cls : 'roo-navigation-bar-text'
28424 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28425 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28427 var topText = new Roo.bootstrap.Element({
28429 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28432 var bottomText = new Roo.bootstrap.Element({
28434 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28437 topText.onRender(top.el, null);
28438 bottomText.onRender(bottom.el, null);
28441 item.bottomEl = bottom;
28444 this.barItems.push(item);
28449 getActive : function()
28451 var active = false;
28453 Roo.each(this.barItems, function(v){
28455 if (!v.isActive()) {
28467 setActiveItem : function(item)
28471 Roo.each(this.barItems, function(v){
28472 if (v.rid == item.rid) {
28476 if (v.isActive()) {
28477 v.setActive(false);
28482 item.setActive(true);
28484 this.fireEvent('changed', this, item, prev);
28487 getBarItem: function(rid)
28491 Roo.each(this.barItems, function(e) {
28492 if (e.rid != rid) {
28503 indexOfItem : function(item)
28507 Roo.each(this.barItems, function(v, i){
28509 if (v.rid != item.rid) {
28520 setActiveNext : function()
28522 var i = this.indexOfItem(this.getActive());
28524 if (i > this.barItems.length) {
28528 this.setActiveItem(this.barItems[i+1]);
28531 setActivePrev : function()
28533 var i = this.indexOfItem(this.getActive());
28539 this.setActiveItem(this.barItems[i-1]);
28542 format : function()
28544 if(!this.barItems.length){
28548 var width = 100 / this.barItems.length;
28550 Roo.each(this.barItems, function(i){
28551 i.el.setStyle('width', width + '%');
28552 i.topEl.el.setStyle('width', width + '%');
28553 i.bottomEl.el.setStyle('width', width + '%');
28562 * Nav Progress Item
28567 * @class Roo.bootstrap.NavProgressItem
28568 * @extends Roo.bootstrap.Component
28569 * Bootstrap NavProgressItem class
28570 * @cfg {String} rid the reference id
28571 * @cfg {Boolean} active (true|false) Is item active default false
28572 * @cfg {Boolean} disabled (true|false) Is item active default false
28573 * @cfg {String} html
28574 * @cfg {String} position (top|bottom) text position default bottom
28575 * @cfg {String} icon show icon instead of number
28578 * Create a new NavProgressItem
28579 * @param {Object} config The config object
28581 Roo.bootstrap.NavProgressItem = function(config){
28582 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28587 * The raw click event for the entire grid.
28588 * @param {Roo.bootstrap.NavProgressItem} this
28589 * @param {Roo.EventObject} e
28596 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
28602 position : 'bottom',
28605 getAutoCreate : function()
28607 var iconCls = 'roo-navigation-bar-item-icon';
28609 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28613 cls: 'roo-navigation-bar-item',
28623 cfg.cls += ' active';
28626 cfg.cls += ' disabled';
28632 disable : function()
28634 this.setDisabled(true);
28637 enable : function()
28639 this.setDisabled(false);
28642 initEvents: function()
28644 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28646 this.iconEl.on('click', this.onClick, this);
28649 onClick : function(e)
28651 e.preventDefault();
28657 if(this.fireEvent('click', this, e) === false){
28661 this.parent().setActiveItem(this);
28664 isActive: function ()
28666 return this.active;
28669 setActive : function(state)
28671 if(this.active == state){
28675 this.active = state;
28678 this.el.addClass('active');
28682 this.el.removeClass('active');
28687 setDisabled : function(state)
28689 if(this.disabled == state){
28693 this.disabled = state;
28696 this.el.addClass('disabled');
28700 this.el.removeClass('disabled');
28703 tooltipEl : function()
28705 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28718 * @class Roo.bootstrap.FieldLabel
28719 * @extends Roo.bootstrap.Component
28720 * Bootstrap FieldLabel class
28721 * @cfg {String} html contents of the element
28722 * @cfg {String} tag tag of the element default label
28723 * @cfg {String} cls class of the element
28724 * @cfg {String} target label target
28725 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28726 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28727 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28728 * @cfg {String} iconTooltip default "This field is required"
28731 * Create a new FieldLabel
28732 * @param {Object} config The config object
28735 Roo.bootstrap.FieldLabel = function(config){
28736 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28741 * Fires after the field has been marked as invalid.
28742 * @param {Roo.form.FieldLabel} this
28743 * @param {String} msg The validation message
28748 * Fires after the field has been validated with no errors.
28749 * @param {Roo.form.FieldLabel} this
28755 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28762 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28763 validClass : 'text-success fa fa-lg fa-check',
28764 iconTooltip : 'This field is required',
28766 getAutoCreate : function(){
28770 cls : 'roo-bootstrap-field-label ' + this.cls,
28776 tooltip : this.iconTooltip
28788 initEvents: function()
28790 Roo.bootstrap.Element.superclass.initEvents.call(this);
28792 this.iconEl = this.el.select('i', true).first();
28794 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28796 Roo.bootstrap.FieldLabel.register(this);
28800 * Mark this field as valid
28802 markValid : function()
28804 this.iconEl.show();
28806 this.iconEl.removeClass(this.invalidClass);
28808 this.iconEl.addClass(this.validClass);
28810 this.fireEvent('valid', this);
28814 * Mark this field as invalid
28815 * @param {String} msg The validation message
28817 markInvalid : function(msg)
28819 this.iconEl.show();
28821 this.iconEl.removeClass(this.validClass);
28823 this.iconEl.addClass(this.invalidClass);
28825 this.fireEvent('invalid', this, msg);
28831 Roo.apply(Roo.bootstrap.FieldLabel, {
28836 * register a FieldLabel Group
28837 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28839 register : function(label)
28841 if(this.groups.hasOwnProperty(label.target)){
28845 this.groups[label.target] = label;
28849 * fetch a FieldLabel Group based on the target
28850 * @param {string} target
28851 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28853 get: function(target) {
28854 if (typeof(this.groups[target]) == 'undefined') {
28858 return this.groups[target] ;
28867 * page DateSplitField.
28873 * @class Roo.bootstrap.DateSplitField
28874 * @extends Roo.bootstrap.Component
28875 * Bootstrap DateSplitField class
28876 * @cfg {string} fieldLabel - the label associated
28877 * @cfg {Number} labelWidth set the width of label (0-12)
28878 * @cfg {String} labelAlign (top|left)
28879 * @cfg {Boolean} dayAllowBlank (true|false) default false
28880 * @cfg {Boolean} monthAllowBlank (true|false) default false
28881 * @cfg {Boolean} yearAllowBlank (true|false) default false
28882 * @cfg {string} dayPlaceholder
28883 * @cfg {string} monthPlaceholder
28884 * @cfg {string} yearPlaceholder
28885 * @cfg {string} dayFormat default 'd'
28886 * @cfg {string} monthFormat default 'm'
28887 * @cfg {string} yearFormat default 'Y'
28891 * Create a new DateSplitField
28892 * @param {Object} config The config object
28895 Roo.bootstrap.DateSplitField = function(config){
28896 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28902 * getting the data of years
28903 * @param {Roo.bootstrap.DateSplitField} this
28904 * @param {Object} years
28909 * getting the data of days
28910 * @param {Roo.bootstrap.DateSplitField} this
28911 * @param {Object} days
28916 * Fires after the field has been marked as invalid.
28917 * @param {Roo.form.Field} this
28918 * @param {String} msg The validation message
28923 * Fires after the field has been validated with no errors.
28924 * @param {Roo.form.Field} this
28930 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28933 labelAlign : 'top',
28935 dayAllowBlank : false,
28936 monthAllowBlank : false,
28937 yearAllowBlank : false,
28938 dayPlaceholder : '',
28939 monthPlaceholder : '',
28940 yearPlaceholder : '',
28944 isFormField : true,
28946 getAutoCreate : function()
28950 cls : 'row roo-date-split-field-group',
28955 cls : 'form-hidden-field roo-date-split-field-group-value',
28961 if(this.fieldLabel){
28964 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28968 html : this.fieldLabel
28974 Roo.each(['day', 'month', 'year'], function(t){
28977 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28984 inputEl: function ()
28986 return this.el.select('.roo-date-split-field-group-value', true).first();
28989 onRender : function(ct, position)
28993 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28995 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28997 this.dayField = new Roo.bootstrap.ComboBox({
28998 allowBlank : this.dayAllowBlank,
28999 alwaysQuery : true,
29000 displayField : 'value',
29003 forceSelection : true,
29005 placeholder : this.dayPlaceholder,
29006 selectOnFocus : true,
29007 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29008 triggerAction : 'all',
29010 valueField : 'value',
29011 store : new Roo.data.SimpleStore({
29012 data : (function() {
29014 _this.fireEvent('days', _this, days);
29017 fields : [ 'value' ]
29020 select : function (_self, record, index)
29022 _this.setValue(_this.getValue());
29027 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29029 this.monthField = new Roo.bootstrap.MonthField({
29030 after : '<i class=\"fa fa-calendar\"></i>',
29031 allowBlank : this.monthAllowBlank,
29032 placeholder : this.monthPlaceholder,
29035 render : function (_self)
29037 this.el.select('span.input-group-addon', true).first().on('click', function(e){
29038 e.preventDefault();
29042 select : function (_self, oldvalue, newvalue)
29044 _this.setValue(_this.getValue());
29049 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29051 this.yearField = new Roo.bootstrap.ComboBox({
29052 allowBlank : this.yearAllowBlank,
29053 alwaysQuery : true,
29054 displayField : 'value',
29057 forceSelection : true,
29059 placeholder : this.yearPlaceholder,
29060 selectOnFocus : true,
29061 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29062 triggerAction : 'all',
29064 valueField : 'value',
29065 store : new Roo.data.SimpleStore({
29066 data : (function() {
29068 _this.fireEvent('years', _this, years);
29071 fields : [ 'value' ]
29074 select : function (_self, record, index)
29076 _this.setValue(_this.getValue());
29081 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29084 setValue : function(v, format)
29086 this.inputEl.dom.value = v;
29088 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29090 var d = Date.parseDate(v, f);
29097 this.setDay(d.format(this.dayFormat));
29098 this.setMonth(d.format(this.monthFormat));
29099 this.setYear(d.format(this.yearFormat));
29106 setDay : function(v)
29108 this.dayField.setValue(v);
29109 this.inputEl.dom.value = this.getValue();
29114 setMonth : function(v)
29116 this.monthField.setValue(v, true);
29117 this.inputEl.dom.value = this.getValue();
29122 setYear : function(v)
29124 this.yearField.setValue(v);
29125 this.inputEl.dom.value = this.getValue();
29130 getDay : function()
29132 return this.dayField.getValue();
29135 getMonth : function()
29137 return this.monthField.getValue();
29140 getYear : function()
29142 return this.yearField.getValue();
29145 getValue : function()
29147 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29149 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29159 this.inputEl.dom.value = '';
29164 validate : function()
29166 var d = this.dayField.validate();
29167 var m = this.monthField.validate();
29168 var y = this.yearField.validate();
29173 (!this.dayAllowBlank && !d) ||
29174 (!this.monthAllowBlank && !m) ||
29175 (!this.yearAllowBlank && !y)
29180 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29189 this.markInvalid();
29194 markValid : function()
29197 var label = this.el.select('label', true).first();
29198 var icon = this.el.select('i.fa-star', true).first();
29204 this.fireEvent('valid', this);
29208 * Mark this field as invalid
29209 * @param {String} msg The validation message
29211 markInvalid : function(msg)
29214 var label = this.el.select('label', true).first();
29215 var icon = this.el.select('i.fa-star', true).first();
29217 if(label && !icon){
29218 this.el.select('.roo-date-split-field-label', true).createChild({
29220 cls : 'text-danger fa fa-lg fa-star',
29221 tooltip : 'This field is required',
29222 style : 'margin-right:5px;'
29226 this.fireEvent('invalid', this, msg);
29229 clearInvalid : function()
29231 var label = this.el.select('label', true).first();
29232 var icon = this.el.select('i.fa-star', true).first();
29238 this.fireEvent('valid', this);
29241 getName: function()
29251 * http://masonry.desandro.com
29253 * The idea is to render all the bricks based on vertical width...
29255 * The original code extends 'outlayer' - we might need to use that....
29261 * @class Roo.bootstrap.LayoutMasonry
29262 * @extends Roo.bootstrap.Component
29263 * Bootstrap Layout Masonry class
29266 * Create a new Element
29267 * @param {Object} config The config object
29270 Roo.bootstrap.LayoutMasonry = function(config){
29271 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29277 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
29280 * @cfg {Boolean} isLayoutInstant = no animation?
29282 isLayoutInstant : false, // needed?
29285 * @cfg {Number} boxWidth width of the columns
29290 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
29295 * @cfg {Number} padWidth padding below box..
29300 * @cfg {Number} gutter gutter width..
29305 * @cfg {Number} maxCols maximum number of columns
29311 * @cfg {Boolean} isAutoInitial defalut true
29313 isAutoInitial : true,
29318 * @cfg {Boolean} isHorizontal defalut false
29320 isHorizontal : false,
29322 currentSize : null,
29328 bricks: null, //CompositeElement
29332 _isLayoutInited : false,
29334 // isAlternative : false, // only use for vertical layout...
29337 * @cfg {Number} alternativePadWidth padding below box..
29339 alternativePadWidth : 50,
29341 getAutoCreate : function(){
29345 cls: 'blog-masonary-wrapper ' + this.cls,
29347 cls : 'mas-boxes masonary'
29354 getChildContainer: function( )
29356 if (this.boxesEl) {
29357 return this.boxesEl;
29360 this.boxesEl = this.el.select('.mas-boxes').first();
29362 return this.boxesEl;
29366 initEvents : function()
29370 if(this.isAutoInitial){
29371 Roo.log('hook children rendered');
29372 this.on('childrenrendered', function() {
29373 Roo.log('children rendered');
29379 initial : function()
29381 this.currentSize = this.el.getBox(true);
29383 Roo.EventManager.onWindowResize(this.resize, this);
29385 if(!this.isAutoInitial){
29393 //this.layout.defer(500,this);
29397 resize : function()
29401 var cs = this.el.getBox(true);
29403 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29404 Roo.log("no change in with or X");
29408 this.currentSize = cs;
29414 layout : function()
29416 this._resetLayout();
29418 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29420 this.layoutItems( isInstant );
29422 this._isLayoutInited = true;
29426 _resetLayout : function()
29428 if(this.isHorizontal){
29429 this.horizontalMeasureColumns();
29433 this.verticalMeasureColumns();
29437 verticalMeasureColumns : function()
29439 this.getContainerWidth();
29441 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29442 // this.colWidth = Math.floor(this.containerWidth * 0.8);
29446 var boxWidth = this.boxWidth + this.padWidth;
29448 if(this.containerWidth < this.boxWidth){
29449 boxWidth = this.containerWidth
29452 var containerWidth = this.containerWidth;
29454 var cols = Math.floor(containerWidth / boxWidth);
29456 this.cols = Math.max( cols, 1 );
29458 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29460 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29462 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29464 this.colWidth = boxWidth + avail - this.padWidth;
29466 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29467 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
29470 horizontalMeasureColumns : function()
29472 this.getContainerWidth();
29474 var boxWidth = this.boxWidth;
29476 if(this.containerWidth < boxWidth){
29477 boxWidth = this.containerWidth;
29480 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29482 this.el.setHeight(boxWidth);
29486 getContainerWidth : function()
29488 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
29491 layoutItems : function( isInstant )
29493 var items = Roo.apply([], this.bricks);
29495 if(this.isHorizontal){
29496 this._horizontalLayoutItems( items , isInstant );
29500 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29501 // this._verticalAlternativeLayoutItems( items , isInstant );
29505 this._verticalLayoutItems( items , isInstant );
29509 _verticalLayoutItems : function ( items , isInstant)
29511 if ( !items || !items.length ) {
29516 ['xs', 'xs', 'xs', 'tall'],
29517 ['xs', 'xs', 'tall'],
29518 ['xs', 'xs', 'sm'],
29519 ['xs', 'xs', 'xs'],
29525 ['sm', 'xs', 'xs'],
29529 ['tall', 'xs', 'xs', 'xs'],
29530 ['tall', 'xs', 'xs'],
29542 Roo.each(items, function(item, k){
29544 switch (item.size) {
29545 // these layouts take up a full box,
29556 boxes.push([item]);
29579 var filterPattern = function(box, length)
29587 var pattern = box.slice(0, length);
29591 Roo.each(pattern, function(i){
29592 format.push(i.size);
29595 Roo.each(standard, function(s){
29597 if(String(s) != String(format)){
29606 if(!match && length == 1){
29611 filterPattern(box, length - 1);
29615 queue.push(pattern);
29617 box = box.slice(length, box.length);
29619 filterPattern(box, 4);
29625 Roo.each(boxes, function(box, k){
29631 if(box.length == 1){
29636 filterPattern(box, 4);
29640 this._processVerticalLayoutQueue( queue, isInstant );
29644 // _verticalAlternativeLayoutItems : function( items , isInstant )
29646 // if ( !items || !items.length ) {
29650 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
29654 _horizontalLayoutItems : function ( items , isInstant)
29656 if ( !items || !items.length || items.length < 3) {
29662 var eItems = items.slice(0, 3);
29664 items = items.slice(3, items.length);
29667 ['xs', 'xs', 'xs', 'wide'],
29668 ['xs', 'xs', 'wide'],
29669 ['xs', 'xs', 'sm'],
29670 ['xs', 'xs', 'xs'],
29676 ['sm', 'xs', 'xs'],
29680 ['wide', 'xs', 'xs', 'xs'],
29681 ['wide', 'xs', 'xs'],
29694 Roo.each(items, function(item, k){
29696 switch (item.size) {
29707 boxes.push([item]);
29731 var filterPattern = function(box, length)
29739 var pattern = box.slice(0, length);
29743 Roo.each(pattern, function(i){
29744 format.push(i.size);
29747 Roo.each(standard, function(s){
29749 if(String(s) != String(format)){
29758 if(!match && length == 1){
29763 filterPattern(box, length - 1);
29767 queue.push(pattern);
29769 box = box.slice(length, box.length);
29771 filterPattern(box, 4);
29777 Roo.each(boxes, function(box, k){
29783 if(box.length == 1){
29788 filterPattern(box, 4);
29795 var pos = this.el.getBox(true);
29799 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29801 var hit_end = false;
29803 Roo.each(queue, function(box){
29807 Roo.each(box, function(b){
29809 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29819 Roo.each(box, function(b){
29821 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29824 mx = Math.max(mx, b.x);
29828 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29832 Roo.each(box, function(b){
29834 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29848 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29851 /** Sets position of item in DOM
29852 * @param {Element} item
29853 * @param {Number} x - horizontal position
29854 * @param {Number} y - vertical position
29855 * @param {Boolean} isInstant - disables transitions
29857 _processVerticalLayoutQueue : function( queue, isInstant )
29859 var pos = this.el.getBox(true);
29864 for (var i = 0; i < this.cols; i++){
29868 Roo.each(queue, function(box, k){
29870 var col = k % this.cols;
29872 Roo.each(box, function(b,kk){
29874 b.el.position('absolute');
29876 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29877 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29879 if(b.size == 'md-left' || b.size == 'md-right'){
29880 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29881 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29884 b.el.setWidth(width);
29885 b.el.setHeight(height);
29887 b.el.select('iframe',true).setSize(width,height);
29891 for (var i = 0; i < this.cols; i++){
29893 if(maxY[i] < maxY[col]){
29898 col = Math.min(col, i);
29902 x = pos.x + col * (this.colWidth + this.padWidth);
29906 var positions = [];
29908 switch (box.length){
29910 positions = this.getVerticalOneBoxColPositions(x, y, box);
29913 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29916 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29919 positions = this.getVerticalFourBoxColPositions(x, y, box);
29925 Roo.each(box, function(b,kk){
29927 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29929 var sz = b.el.getSize();
29931 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29939 for (var i = 0; i < this.cols; i++){
29940 mY = Math.max(mY, maxY[i]);
29943 this.el.setHeight(mY - pos.y);
29947 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29949 // var pos = this.el.getBox(true);
29952 // var maxX = pos.right;
29954 // var maxHeight = 0;
29956 // Roo.each(items, function(item, k){
29960 // item.el.position('absolute');
29962 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29964 // item.el.setWidth(width);
29966 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29968 // item.el.setHeight(height);
29971 // item.el.setXY([x, y], isInstant ? false : true);
29973 // item.el.setXY([maxX - width, y], isInstant ? false : true);
29976 // y = y + height + this.alternativePadWidth;
29978 // maxHeight = maxHeight + height + this.alternativePadWidth;
29982 // this.el.setHeight(maxHeight);
29986 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29988 var pos = this.el.getBox(true);
29993 var maxX = pos.right;
29995 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29997 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29999 Roo.each(queue, function(box, k){
30001 Roo.each(box, function(b, kk){
30003 b.el.position('absolute');
30005 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30006 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30008 if(b.size == 'md-left' || b.size == 'md-right'){
30009 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30010 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30013 b.el.setWidth(width);
30014 b.el.setHeight(height);
30022 var positions = [];
30024 switch (box.length){
30026 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30029 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30032 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30035 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30041 Roo.each(box, function(b,kk){
30043 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30045 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30053 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30055 Roo.each(eItems, function(b,k){
30057 b.size = (k == 0) ? 'sm' : 'xs';
30058 b.x = (k == 0) ? 2 : 1;
30059 b.y = (k == 0) ? 2 : 1;
30061 b.el.position('absolute');
30063 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30065 b.el.setWidth(width);
30067 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30069 b.el.setHeight(height);
30073 var positions = [];
30076 x : maxX - this.unitWidth * 2 - this.gutter,
30081 x : maxX - this.unitWidth,
30082 y : minY + (this.unitWidth + this.gutter) * 2
30086 x : maxX - this.unitWidth * 3 - this.gutter * 2,
30090 Roo.each(eItems, function(b,k){
30092 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30098 getVerticalOneBoxColPositions : function(x, y, box)
30102 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30104 if(box[0].size == 'md-left'){
30108 if(box[0].size == 'md-right'){
30113 x : x + (this.unitWidth + this.gutter) * rand,
30120 getVerticalTwoBoxColPositions : function(x, y, box)
30124 if(box[0].size == 'xs'){
30128 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30132 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30146 x : x + (this.unitWidth + this.gutter) * 2,
30147 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30154 getVerticalThreeBoxColPositions : function(x, y, box)
30158 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30166 x : x + (this.unitWidth + this.gutter) * 1,
30171 x : x + (this.unitWidth + this.gutter) * 2,
30179 if(box[0].size == 'xs' && box[1].size == 'xs'){
30188 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30192 x : x + (this.unitWidth + this.gutter) * 1,
30206 x : x + (this.unitWidth + this.gutter) * 2,
30211 x : x + (this.unitWidth + this.gutter) * 2,
30212 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30219 getVerticalFourBoxColPositions : function(x, y, box)
30223 if(box[0].size == 'xs'){
30232 y : y + (this.unitHeight + this.gutter) * 1
30237 y : y + (this.unitHeight + this.gutter) * 2
30241 x : x + (this.unitWidth + this.gutter) * 1,
30255 x : x + (this.unitWidth + this.gutter) * 2,
30260 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30261 y : y + (this.unitHeight + this.gutter) * 1
30265 x : x + (this.unitWidth + this.gutter) * 2,
30266 y : y + (this.unitWidth + this.gutter) * 2
30273 getHorizontalOneBoxColPositions : function(maxX, minY, box)
30277 if(box[0].size == 'md-left'){
30279 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30286 if(box[0].size == 'md-right'){
30288 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30289 y : minY + (this.unitWidth + this.gutter) * 1
30295 var rand = Math.floor(Math.random() * (4 - box[0].y));
30298 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30299 y : minY + (this.unitWidth + this.gutter) * rand
30306 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30310 if(box[0].size == 'xs'){
30313 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30318 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30319 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30327 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30332 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30333 y : minY + (this.unitWidth + this.gutter) * 2
30340 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30344 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30347 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30352 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30353 y : minY + (this.unitWidth + this.gutter) * 1
30357 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30358 y : minY + (this.unitWidth + this.gutter) * 2
30365 if(box[0].size == 'xs' && box[1].size == 'xs'){
30368 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30373 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30378 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30379 y : minY + (this.unitWidth + this.gutter) * 1
30387 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30392 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30393 y : minY + (this.unitWidth + this.gutter) * 2
30397 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30398 y : minY + (this.unitWidth + this.gutter) * 2
30405 getHorizontalFourBoxColPositions : function(maxX, minY, box)
30409 if(box[0].size == 'xs'){
30412 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].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),
30422 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),
30427 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30428 y : minY + (this.unitWidth + this.gutter) * 1
30436 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30441 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].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),
30447 y : minY + (this.unitWidth + this.gutter) * 2
30451 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),
30452 y : minY + (this.unitWidth + this.gutter) * 2
30466 * http://masonry.desandro.com
30468 * The idea is to render all the bricks based on vertical width...
30470 * The original code extends 'outlayer' - we might need to use that....
30476 * @class Roo.bootstrap.LayoutMasonryAuto
30477 * @extends Roo.bootstrap.Component
30478 * Bootstrap Layout Masonry class
30481 * Create a new Element
30482 * @param {Object} config The config object
30485 Roo.bootstrap.LayoutMasonryAuto = function(config){
30486 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30489 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
30492 * @cfg {Boolean} isFitWidth - resize the width..
30494 isFitWidth : false, // options..
30496 * @cfg {Boolean} isOriginLeft = left align?
30498 isOriginLeft : true,
30500 * @cfg {Boolean} isOriginTop = top align?
30502 isOriginTop : false,
30504 * @cfg {Boolean} isLayoutInstant = no animation?
30506 isLayoutInstant : false, // needed?
30508 * @cfg {Boolean} isResizingContainer = not sure if this is used..
30510 isResizingContainer : true,
30512 * @cfg {Number} columnWidth width of the columns
30518 * @cfg {Number} maxCols maximum number of columns
30523 * @cfg {Number} padHeight padding below box..
30529 * @cfg {Boolean} isAutoInitial defalut true
30532 isAutoInitial : true,
30538 initialColumnWidth : 0,
30539 currentSize : null,
30541 colYs : null, // array.
30548 bricks: null, //CompositeElement
30549 cols : 0, // array?
30550 // element : null, // wrapped now this.el
30551 _isLayoutInited : null,
30554 getAutoCreate : function(){
30558 cls: 'blog-masonary-wrapper ' + this.cls,
30560 cls : 'mas-boxes masonary'
30567 getChildContainer: function( )
30569 if (this.boxesEl) {
30570 return this.boxesEl;
30573 this.boxesEl = this.el.select('.mas-boxes').first();
30575 return this.boxesEl;
30579 initEvents : function()
30583 if(this.isAutoInitial){
30584 Roo.log('hook children rendered');
30585 this.on('childrenrendered', function() {
30586 Roo.log('children rendered');
30593 initial : function()
30595 this.reloadItems();
30597 this.currentSize = this.el.getBox(true);
30599 /// was window resize... - let's see if this works..
30600 Roo.EventManager.onWindowResize(this.resize, this);
30602 if(!this.isAutoInitial){
30607 this.layout.defer(500,this);
30610 reloadItems: function()
30612 this.bricks = this.el.select('.masonry-brick', true);
30614 this.bricks.each(function(b) {
30615 //Roo.log(b.getSize());
30616 if (!b.attr('originalwidth')) {
30617 b.attr('originalwidth', b.getSize().width);
30622 Roo.log(this.bricks.elements.length);
30625 resize : function()
30628 var cs = this.el.getBox(true);
30630 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30631 Roo.log("no change in with or X");
30634 this.currentSize = cs;
30638 layout : function()
30641 this._resetLayout();
30642 //this._manageStamps();
30644 // don't animate first layout
30645 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30646 this.layoutItems( isInstant );
30648 // flag for initalized
30649 this._isLayoutInited = true;
30652 layoutItems : function( isInstant )
30654 //var items = this._getItemsForLayout( this.items );
30655 // original code supports filtering layout items.. we just ignore it..
30657 this._layoutItems( this.bricks , isInstant );
30659 this._postLayout();
30661 _layoutItems : function ( items , isInstant)
30663 //this.fireEvent( 'layout', this, items );
30666 if ( !items || !items.elements.length ) {
30667 // no items, emit event with empty array
30672 items.each(function(item) {
30673 Roo.log("layout item");
30675 // get x/y object from method
30676 var position = this._getItemLayoutPosition( item );
30678 position.item = item;
30679 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30680 queue.push( position );
30683 this._processLayoutQueue( queue );
30685 /** Sets position of item in DOM
30686 * @param {Element} item
30687 * @param {Number} x - horizontal position
30688 * @param {Number} y - vertical position
30689 * @param {Boolean} isInstant - disables transitions
30691 _processLayoutQueue : function( queue )
30693 for ( var i=0, len = queue.length; i < len; i++ ) {
30694 var obj = queue[i];
30695 obj.item.position('absolute');
30696 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30702 * Any logic you want to do after each layout,
30703 * i.e. size the container
30705 _postLayout : function()
30707 this.resizeContainer();
30710 resizeContainer : function()
30712 if ( !this.isResizingContainer ) {
30715 var size = this._getContainerSize();
30717 this.el.setSize(size.width,size.height);
30718 this.boxesEl.setSize(size.width,size.height);
30724 _resetLayout : function()
30726 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30727 this.colWidth = this.el.getWidth();
30728 //this.gutter = this.el.getWidth();
30730 this.measureColumns();
30736 this.colYs.push( 0 );
30742 measureColumns : function()
30744 this.getContainerWidth();
30745 // if columnWidth is 0, default to outerWidth of first item
30746 if ( !this.columnWidth ) {
30747 var firstItem = this.bricks.first();
30748 Roo.log(firstItem);
30749 this.columnWidth = this.containerWidth;
30750 if (firstItem && firstItem.attr('originalwidth') ) {
30751 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30753 // columnWidth fall back to item of first element
30754 Roo.log("set column width?");
30755 this.initialColumnWidth = this.columnWidth ;
30757 // if first elem has no width, default to size of container
30762 if (this.initialColumnWidth) {
30763 this.columnWidth = this.initialColumnWidth;
30768 // column width is fixed at the top - however if container width get's smaller we should
30771 // this bit calcs how man columns..
30773 var columnWidth = this.columnWidth += this.gutter;
30775 // calculate columns
30776 var containerWidth = this.containerWidth + this.gutter;
30778 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30779 // fix rounding errors, typically with gutters
30780 var excess = columnWidth - containerWidth % columnWidth;
30783 // if overshoot is less than a pixel, round up, otherwise floor it
30784 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30785 cols = Math[ mathMethod ]( cols );
30786 this.cols = Math.max( cols, 1 );
30787 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30789 // padding positioning..
30790 var totalColWidth = this.cols * this.columnWidth;
30791 var padavail = this.containerWidth - totalColWidth;
30792 // so for 2 columns - we need 3 'pads'
30794 var padNeeded = (1+this.cols) * this.padWidth;
30796 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30798 this.columnWidth += padExtra
30799 //this.padWidth = Math.floor(padavail / ( this.cols));
30801 // adjust colum width so that padding is fixed??
30803 // we have 3 columns ... total = width * 3
30804 // we have X left over... that should be used by
30806 //if (this.expandC) {
30814 getContainerWidth : function()
30816 /* // container is parent if fit width
30817 var container = this.isFitWidth ? this.element.parentNode : this.element;
30818 // check that this.size and size are there
30819 // IE8 triggers resize on body size change, so they might not be
30821 var size = getSize( container ); //FIXME
30822 this.containerWidth = size && size.innerWidth; //FIXME
30825 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30829 _getItemLayoutPosition : function( item ) // what is item?
30831 // we resize the item to our columnWidth..
30833 item.setWidth(this.columnWidth);
30834 item.autoBoxAdjust = false;
30836 var sz = item.getSize();
30838 // how many columns does this brick span
30839 var remainder = this.containerWidth % this.columnWidth;
30841 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30842 // round if off by 1 pixel, otherwise use ceil
30843 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30844 colSpan = Math.min( colSpan, this.cols );
30846 // normally this should be '1' as we dont' currently allow multi width columns..
30848 var colGroup = this._getColGroup( colSpan );
30849 // get the minimum Y value from the columns
30850 var minimumY = Math.min.apply( Math, colGroup );
30851 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30853 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30855 // position the brick
30857 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30858 y: this.currentSize.y + minimumY + this.padHeight
30862 // apply setHeight to necessary columns
30863 var setHeight = minimumY + sz.height + this.padHeight;
30864 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30866 var setSpan = this.cols + 1 - colGroup.length;
30867 for ( var i = 0; i < setSpan; i++ ) {
30868 this.colYs[ shortColIndex + i ] = setHeight ;
30875 * @param {Number} colSpan - number of columns the element spans
30876 * @returns {Array} colGroup
30878 _getColGroup : function( colSpan )
30880 if ( colSpan < 2 ) {
30881 // if brick spans only one column, use all the column Ys
30886 // how many different places could this brick fit horizontally
30887 var groupCount = this.cols + 1 - colSpan;
30888 // for each group potential horizontal position
30889 for ( var i = 0; i < groupCount; i++ ) {
30890 // make an array of colY values for that one group
30891 var groupColYs = this.colYs.slice( i, i + colSpan );
30892 // and get the max value of the array
30893 colGroup[i] = Math.max.apply( Math, groupColYs );
30898 _manageStamp : function( stamp )
30900 var stampSize = stamp.getSize();
30901 var offset = stamp.getBox();
30902 // get the columns that this stamp affects
30903 var firstX = this.isOriginLeft ? offset.x : offset.right;
30904 var lastX = firstX + stampSize.width;
30905 var firstCol = Math.floor( firstX / this.columnWidth );
30906 firstCol = Math.max( 0, firstCol );
30908 var lastCol = Math.floor( lastX / this.columnWidth );
30909 // lastCol should not go over if multiple of columnWidth #425
30910 lastCol -= lastX % this.columnWidth ? 0 : 1;
30911 lastCol = Math.min( this.cols - 1, lastCol );
30913 // set colYs to bottom of the stamp
30914 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30917 for ( var i = firstCol; i <= lastCol; i++ ) {
30918 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30923 _getContainerSize : function()
30925 this.maxY = Math.max.apply( Math, this.colYs );
30930 if ( this.isFitWidth ) {
30931 size.width = this._getContainerFitWidth();
30937 _getContainerFitWidth : function()
30939 var unusedCols = 0;
30940 // count unused columns
30943 if ( this.colYs[i] !== 0 ) {
30948 // fit container to columns that have been used
30949 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30952 needsResizeLayout : function()
30954 var previousWidth = this.containerWidth;
30955 this.getContainerWidth();
30956 return previousWidth !== this.containerWidth;
30971 * @class Roo.bootstrap.MasonryBrick
30972 * @extends Roo.bootstrap.Component
30973 * Bootstrap MasonryBrick class
30976 * Create a new MasonryBrick
30977 * @param {Object} config The config object
30980 Roo.bootstrap.MasonryBrick = function(config){
30981 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30987 * When a MasonryBrick is clcik
30988 * @param {Roo.bootstrap.MasonryBrick} this
30989 * @param {Roo.EventObject} e
30995 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
30998 * @cfg {String} title
31002 * @cfg {String} html
31006 * @cfg {String} bgimage
31010 * @cfg {String} videourl
31014 * @cfg {String} cls
31018 * @cfg {String} href
31022 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31027 * @cfg {String} (center|bottom) placetitle
31032 * @cfg {Boolean} isFitContainer defalut true
31034 isFitContainer : true,
31037 * @cfg {Boolean} preventDefault defalut false
31039 preventDefault : false,
31041 getAutoCreate : function()
31043 if(!this.isFitContainer){
31044 return this.getSplitAutoCreate();
31047 var cls = 'masonry-brick masonry-brick-full';
31049 if(this.href.length){
31050 cls += ' masonry-brick-link';
31053 if(this.bgimage.length){
31054 cls += ' masonry-brick-image';
31057 if(!this.html.length){
31058 cls += ' enable-mask';
31062 cls += ' masonry-' + this.size + '-brick';
31065 if(this.placetitle.length){
31067 switch (this.placetitle) {
31069 cls += ' masonry-center-title';
31072 cls += ' masonry-bottom-title';
31079 if(!this.html.length && !this.bgimage.length){
31080 cls += ' masonry-center-title';
31083 if(!this.html.length && this.bgimage.length){
31084 cls += ' masonry-bottom-title';
31089 cls += ' ' + this.cls;
31093 tag: (this.href.length) ? 'a' : 'div',
31098 cls: 'masonry-brick-paragraph',
31104 if(this.href.length){
31105 cfg.href = this.href;
31108 var cn = cfg.cn[0].cn;
31110 if(this.title.length){
31113 cls: 'masonry-brick-title',
31118 if(this.html.length){
31121 cls: 'masonry-brick-text',
31125 if (!this.title.length && !this.html.length) {
31126 cfg.cn[0].cls += ' hide';
31129 if(this.bgimage.length){
31132 cls: 'masonry-brick-image-view',
31137 if(this.videourl.length){
31138 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31139 // youtube support only?
31142 cls: 'masonry-brick-image-view',
31145 allowfullscreen : true
31153 cls: 'masonry-brick-mask'
31160 getSplitAutoCreate : function()
31162 var cls = 'masonry-brick masonry-brick-split';
31164 if(this.href.length){
31165 cls += ' masonry-brick-link';
31168 if(this.bgimage.length){
31169 cls += ' masonry-brick-image';
31173 cls += ' masonry-' + this.size + '-brick';
31176 switch (this.placetitle) {
31178 cls += ' masonry-center-title';
31181 cls += ' masonry-bottom-title';
31184 if(!this.bgimage.length){
31185 cls += ' masonry-center-title';
31188 if(this.bgimage.length){
31189 cls += ' masonry-bottom-title';
31195 cls += ' ' + this.cls;
31199 tag: (this.href.length) ? 'a' : 'div',
31204 cls: 'masonry-brick-split-head',
31208 cls: 'masonry-brick-paragraph',
31215 cls: 'masonry-brick-split-body',
31221 if(this.href.length){
31222 cfg.href = this.href;
31225 if(this.title.length){
31226 cfg.cn[0].cn[0].cn.push({
31228 cls: 'masonry-brick-title',
31233 if(this.html.length){
31234 cfg.cn[1].cn.push({
31236 cls: 'masonry-brick-text',
31241 if(this.bgimage.length){
31242 cfg.cn[0].cn.push({
31244 cls: 'masonry-brick-image-view',
31249 if(this.videourl.length){
31250 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31251 // youtube support only?
31252 cfg.cn[0].cn.cn.push({
31254 cls: 'masonry-brick-image-view',
31257 allowfullscreen : true
31264 initEvents: function()
31266 switch (this.size) {
31299 this.el.on('touchstart', this.onTouchStart, this);
31300 this.el.on('touchmove', this.onTouchMove, this);
31301 this.el.on('touchend', this.onTouchEnd, this);
31302 this.el.on('contextmenu', this.onContextMenu, this);
31304 this.el.on('mouseenter' ,this.enter, this);
31305 this.el.on('mouseleave', this.leave, this);
31306 this.el.on('click', this.onClick, this);
31309 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31310 this.parent().bricks.push(this);
31315 onClick: function(e, el)
31317 var time = this.endTimer - this.startTimer;
31321 e.preventDefault();
31326 if(!this.preventDefault){
31330 e.preventDefault();
31331 this.fireEvent('click', this);
31334 enter: function(e, el)
31336 e.preventDefault();
31338 if(!this.isFitContainer){
31342 if(this.bgimage.length && this.html.length){
31343 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31347 leave: function(e, el)
31349 e.preventDefault();
31351 if(!this.isFitContainer){
31355 if(this.bgimage.length && this.html.length){
31356 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31360 onTouchStart: function(e, el)
31362 // e.preventDefault();
31364 this.touchmoved = false;
31366 if(!this.isFitContainer){
31370 if(!this.bgimage.length || !this.html.length){
31374 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31376 this.timer = new Date().getTime();
31380 onTouchMove: function(e, el)
31382 this.touchmoved = true;
31385 onContextMenu : function(e,el)
31387 e.preventDefault();
31388 e.stopPropagation();
31392 onTouchEnd: function(e, el)
31394 // e.preventDefault();
31396 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31403 if(!this.bgimage.length || !this.html.length){
31405 if(this.href.length){
31406 window.location.href = this.href;
31412 if(!this.isFitContainer){
31416 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31418 window.location.href = this.href;
31433 * @class Roo.bootstrap.Brick
31434 * @extends Roo.bootstrap.Component
31435 * Bootstrap Brick class
31438 * Create a new Brick
31439 * @param {Object} config The config object
31442 Roo.bootstrap.Brick = function(config){
31443 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31449 * When a Brick is click
31450 * @param {Roo.bootstrap.Brick} this
31451 * @param {Roo.EventObject} e
31457 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
31460 * @cfg {String} title
31464 * @cfg {String} html
31468 * @cfg {String} bgimage
31472 * @cfg {String} cls
31476 * @cfg {String} href
31480 * @cfg {String} video
31484 * @cfg {Boolean} square
31488 getAutoCreate : function()
31490 var cls = 'roo-brick';
31492 if(this.href.length){
31493 cls += ' roo-brick-link';
31496 if(this.bgimage.length){
31497 cls += ' roo-brick-image';
31500 if(!this.html.length && !this.bgimage.length){
31501 cls += ' roo-brick-center-title';
31504 if(!this.html.length && this.bgimage.length){
31505 cls += ' roo-brick-bottom-title';
31509 cls += ' ' + this.cls;
31513 tag: (this.href.length) ? 'a' : 'div',
31518 cls: 'roo-brick-paragraph',
31524 if(this.href.length){
31525 cfg.href = this.href;
31528 var cn = cfg.cn[0].cn;
31530 if(this.title.length){
31533 cls: 'roo-brick-title',
31538 if(this.html.length){
31541 cls: 'roo-brick-text',
31548 if(this.bgimage.length){
31551 cls: 'roo-brick-image-view',
31559 initEvents: function()
31561 if(this.title.length || this.html.length){
31562 this.el.on('mouseenter' ,this.enter, this);
31563 this.el.on('mouseleave', this.leave, this);
31567 Roo.EventManager.onWindowResize(this.resize, this);
31572 resize : function()
31574 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31576 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31578 if(this.bgimage.length){
31579 var image = this.el.select('.roo-brick-image-view', true).first();
31580 image.setWidth(paragraph.getWidth());
31581 image.setHeight(paragraph.getWidth());
31583 this.el.setHeight(paragraph.getWidth());
31589 enter: function(e, el)
31591 e.preventDefault();
31593 if(this.bgimage.length){
31594 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31595 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31599 leave: function(e, el)
31601 e.preventDefault();
31603 if(this.bgimage.length){
31604 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31605 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31615 * Ext JS Library 1.1.1
31616 * Copyright(c) 2006-2007, Ext JS, LLC.
31618 * Originally Released Under LGPL - original licence link has changed is not relivant.
31621 * <script type="text/javascript">
31626 * @class Roo.bootstrap.SplitBar
31627 * @extends Roo.util.Observable
31628 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31632 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31633 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31634 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31635 split.minSize = 100;
31636 split.maxSize = 600;
31637 split.animate = true;
31638 split.on('moved', splitterMoved);
31641 * Create a new SplitBar
31642 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
31643 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
31644 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31645 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
31646 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31647 position of the SplitBar).
31649 Roo.bootstrap.SplitBar = function(cfg){
31654 // dragElement : elm
31655 // resizingElement: el,
31657 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31658 // placement : Roo.bootstrap.SplitBar.LEFT ,
31659 // existingProxy ???
31662 this.el = Roo.get(cfg.dragElement, true);
31663 this.el.dom.unselectable = "on";
31665 this.resizingEl = Roo.get(cfg.resizingElement, true);
31669 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31670 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31673 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31676 * The minimum size of the resizing element. (Defaults to 0)
31682 * The maximum size of the resizing element. (Defaults to 2000)
31685 this.maxSize = 2000;
31688 * Whether to animate the transition to the new size
31691 this.animate = false;
31694 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31697 this.useShim = false;
31702 if(!cfg.existingProxy){
31704 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31706 this.proxy = Roo.get(cfg.existingProxy).dom;
31709 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31712 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31715 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31718 this.dragSpecs = {};
31721 * @private The adapter to use to positon and resize elements
31723 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31724 this.adapter.init(this);
31726 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31728 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31729 this.el.addClass("roo-splitbar-h");
31732 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31733 this.el.addClass("roo-splitbar-v");
31739 * Fires when the splitter is moved (alias for {@link #event-moved})
31740 * @param {Roo.bootstrap.SplitBar} this
31741 * @param {Number} newSize the new width or height
31746 * Fires when the splitter is moved
31747 * @param {Roo.bootstrap.SplitBar} this
31748 * @param {Number} newSize the new width or height
31752 * @event beforeresize
31753 * Fires before the splitter is dragged
31754 * @param {Roo.bootstrap.SplitBar} this
31756 "beforeresize" : true,
31758 "beforeapply" : true
31761 Roo.util.Observable.call(this);
31764 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31765 onStartProxyDrag : function(x, y){
31766 this.fireEvent("beforeresize", this);
31768 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
31770 o.enableDisplayMode("block");
31771 // all splitbars share the same overlay
31772 Roo.bootstrap.SplitBar.prototype.overlay = o;
31774 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31775 this.overlay.show();
31776 Roo.get(this.proxy).setDisplayed("block");
31777 var size = this.adapter.getElementSize(this);
31778 this.activeMinSize = this.getMinimumSize();;
31779 this.activeMaxSize = this.getMaximumSize();;
31780 var c1 = size - this.activeMinSize;
31781 var c2 = Math.max(this.activeMaxSize - size, 0);
31782 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31783 this.dd.resetConstraints();
31784 this.dd.setXConstraint(
31785 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
31786 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31788 this.dd.setYConstraint(0, 0);
31790 this.dd.resetConstraints();
31791 this.dd.setXConstraint(0, 0);
31792 this.dd.setYConstraint(
31793 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
31794 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31797 this.dragSpecs.startSize = size;
31798 this.dragSpecs.startPoint = [x, y];
31799 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31803 * @private Called after the drag operation by the DDProxy
31805 onEndProxyDrag : function(e){
31806 Roo.get(this.proxy).setDisplayed(false);
31807 var endPoint = Roo.lib.Event.getXY(e);
31809 this.overlay.hide();
31812 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31813 newSize = this.dragSpecs.startSize +
31814 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31815 endPoint[0] - this.dragSpecs.startPoint[0] :
31816 this.dragSpecs.startPoint[0] - endPoint[0]
31819 newSize = this.dragSpecs.startSize +
31820 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31821 endPoint[1] - this.dragSpecs.startPoint[1] :
31822 this.dragSpecs.startPoint[1] - endPoint[1]
31825 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31826 if(newSize != this.dragSpecs.startSize){
31827 if(this.fireEvent('beforeapply', this, newSize) !== false){
31828 this.adapter.setElementSize(this, newSize);
31829 this.fireEvent("moved", this, newSize);
31830 this.fireEvent("resize", this, newSize);
31836 * Get the adapter this SplitBar uses
31837 * @return The adapter object
31839 getAdapter : function(){
31840 return this.adapter;
31844 * Set the adapter this SplitBar uses
31845 * @param {Object} adapter A SplitBar adapter object
31847 setAdapter : function(adapter){
31848 this.adapter = adapter;
31849 this.adapter.init(this);
31853 * Gets the minimum size for the resizing element
31854 * @return {Number} The minimum size
31856 getMinimumSize : function(){
31857 return this.minSize;
31861 * Sets the minimum size for the resizing element
31862 * @param {Number} minSize The minimum size
31864 setMinimumSize : function(minSize){
31865 this.minSize = minSize;
31869 * Gets the maximum size for the resizing element
31870 * @return {Number} The maximum size
31872 getMaximumSize : function(){
31873 return this.maxSize;
31877 * Sets the maximum size for the resizing element
31878 * @param {Number} maxSize The maximum size
31880 setMaximumSize : function(maxSize){
31881 this.maxSize = maxSize;
31885 * Sets the initialize size for the resizing element
31886 * @param {Number} size The initial size
31888 setCurrentSize : function(size){
31889 var oldAnimate = this.animate;
31890 this.animate = false;
31891 this.adapter.setElementSize(this, size);
31892 this.animate = oldAnimate;
31896 * Destroy this splitbar.
31897 * @param {Boolean} removeEl True to remove the element
31899 destroy : function(removeEl){
31901 this.shim.remove();
31904 this.proxy.parentNode.removeChild(this.proxy);
31912 * @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.
31914 Roo.bootstrap.SplitBar.createProxy = function(dir){
31915 var proxy = new Roo.Element(document.createElement("div"));
31916 proxy.unselectable();
31917 var cls = 'roo-splitbar-proxy';
31918 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31919 document.body.appendChild(proxy.dom);
31924 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31925 * Default Adapter. It assumes the splitter and resizing element are not positioned
31926 * elements and only gets/sets the width of the element. Generally used for table based layouts.
31928 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31931 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31932 // do nothing for now
31933 init : function(s){
31937 * Called before drag operations to get the current size of the resizing element.
31938 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31940 getElementSize : function(s){
31941 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31942 return s.resizingEl.getWidth();
31944 return s.resizingEl.getHeight();
31949 * Called after drag operations to set the size of the resizing element.
31950 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31951 * @param {Number} newSize The new size to set
31952 * @param {Function} onComplete A function to be invoked when resizing is complete
31954 setElementSize : function(s, newSize, onComplete){
31955 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31957 s.resizingEl.setWidth(newSize);
31959 onComplete(s, newSize);
31962 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31967 s.resizingEl.setHeight(newSize);
31969 onComplete(s, newSize);
31972 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31979 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31980 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31981 * Adapter that moves the splitter element to align with the resized sizing element.
31982 * Used with an absolute positioned SplitBar.
31983 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31984 * document.body, make sure you assign an id to the body element.
31986 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31987 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31988 this.container = Roo.get(container);
31991 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31992 init : function(s){
31993 this.basic.init(s);
31996 getElementSize : function(s){
31997 return this.basic.getElementSize(s);
32000 setElementSize : function(s, newSize, onComplete){
32001 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32004 moveSplitter : function(s){
32005 var yes = Roo.bootstrap.SplitBar;
32006 switch(s.placement){
32008 s.el.setX(s.resizingEl.getRight());
32011 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32014 s.el.setY(s.resizingEl.getBottom());
32017 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32024 * Orientation constant - Create a vertical SplitBar
32028 Roo.bootstrap.SplitBar.VERTICAL = 1;
32031 * Orientation constant - Create a horizontal SplitBar
32035 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32038 * Placement constant - The resizing element is to the left of the splitter element
32042 Roo.bootstrap.SplitBar.LEFT = 1;
32045 * Placement constant - The resizing element is to the right of the splitter element
32049 Roo.bootstrap.SplitBar.RIGHT = 2;
32052 * Placement constant - The resizing element is positioned above the splitter element
32056 Roo.bootstrap.SplitBar.TOP = 3;
32059 * Placement constant - The resizing element is positioned under splitter element
32063 Roo.bootstrap.SplitBar.BOTTOM = 4;
32064 Roo.namespace("Roo.bootstrap.layout");/*
32066 * Ext JS Library 1.1.1
32067 * Copyright(c) 2006-2007, Ext JS, LLC.
32069 * Originally Released Under LGPL - original licence link has changed is not relivant.
32072 * <script type="text/javascript">
32076 * @class Roo.bootstrap.layout.Manager
32077 * @extends Roo.bootstrap.Component
32078 * Base class for layout managers.
32080 Roo.bootstrap.layout.Manager = function(config)
32082 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32088 /** false to disable window resize monitoring @type Boolean */
32089 this.monitorWindowResize = true;
32094 * Fires when a layout is performed.
32095 * @param {Roo.LayoutManager} this
32099 * @event regionresized
32100 * Fires when the user resizes a region.
32101 * @param {Roo.LayoutRegion} region The resized region
32102 * @param {Number} newSize The new size (width for east/west, height for north/south)
32104 "regionresized" : true,
32106 * @event regioncollapsed
32107 * Fires when a region is collapsed.
32108 * @param {Roo.LayoutRegion} region The collapsed region
32110 "regioncollapsed" : true,
32112 * @event regionexpanded
32113 * Fires when a region is expanded.
32114 * @param {Roo.LayoutRegion} region The expanded region
32116 "regionexpanded" : true
32118 this.updating = false;
32121 this.el = Roo.get(config.el);
32127 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32132 monitorWindowResize : true,
32138 onRender : function(ct, position)
32141 this.el = Roo.get(ct);
32144 //this.fireEvent('render',this);
32148 initEvents: function()
32152 // ie scrollbar fix
32153 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32154 document.body.scroll = "no";
32155 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32156 this.el.position('relative');
32158 this.id = this.el.id;
32159 this.el.addClass("roo-layout-container");
32160 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32161 if(this.el.dom != document.body ) {
32162 this.el.on('resize', this.layout,this);
32163 this.el.on('show', this.layout,this);
32169 * Returns true if this layout is currently being updated
32170 * @return {Boolean}
32172 isUpdating : function(){
32173 return this.updating;
32177 * Suspend the LayoutManager from doing auto-layouts while
32178 * making multiple add or remove calls
32180 beginUpdate : function(){
32181 this.updating = true;
32185 * Restore auto-layouts and optionally disable the manager from performing a layout
32186 * @param {Boolean} noLayout true to disable a layout update
32188 endUpdate : function(noLayout){
32189 this.updating = false;
32195 layout: function(){
32199 onRegionResized : function(region, newSize){
32200 this.fireEvent("regionresized", region, newSize);
32204 onRegionCollapsed : function(region){
32205 this.fireEvent("regioncollapsed", region);
32208 onRegionExpanded : function(region){
32209 this.fireEvent("regionexpanded", region);
32213 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32214 * performs box-model adjustments.
32215 * @return {Object} The size as an object {width: (the width), height: (the height)}
32217 getViewSize : function()
32220 if(this.el.dom != document.body){
32221 size = this.el.getSize();
32223 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32225 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32226 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32231 * Returns the Element this layout is bound to.
32232 * @return {Roo.Element}
32234 getEl : function(){
32239 * Returns the specified region.
32240 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32241 * @return {Roo.LayoutRegion}
32243 getRegion : function(target){
32244 return this.regions[target.toLowerCase()];
32247 onWindowResize : function(){
32248 if(this.monitorWindowResize){
32255 * Ext JS Library 1.1.1
32256 * Copyright(c) 2006-2007, Ext JS, LLC.
32258 * Originally Released Under LGPL - original licence link has changed is not relivant.
32261 * <script type="text/javascript">
32264 * @class Roo.bootstrap.layout.Border
32265 * @extends Roo.bootstrap.layout.Manager
32266 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32267 * please see: examples/bootstrap/nested.html<br><br>
32269 <b>The container the layout is rendered into can be either the body element or any other element.
32270 If it is not the body element, the container needs to either be an absolute positioned element,
32271 or you will need to add "position:relative" to the css of the container. You will also need to specify
32272 the container size if it is not the body element.</b>
32275 * Create a new Border
32276 * @param {Object} config Configuration options
32278 Roo.bootstrap.layout.Border = function(config){
32279 config = config || {};
32280 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32284 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32285 if(config[region]){
32286 config[region].region = region;
32287 this.addRegion(config[region]);
32293 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
32295 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32297 * Creates and adds a new region if it doesn't already exist.
32298 * @param {String} target The target region key (north, south, east, west or center).
32299 * @param {Object} config The regions config object
32300 * @return {BorderLayoutRegion} The new region
32302 addRegion : function(config)
32304 if(!this.regions[config.region]){
32305 var r = this.factory(config);
32306 this.bindRegion(r);
32308 return this.regions[config.region];
32312 bindRegion : function(r){
32313 this.regions[r.config.region] = r;
32315 r.on("visibilitychange", this.layout, this);
32316 r.on("paneladded", this.layout, this);
32317 r.on("panelremoved", this.layout, this);
32318 r.on("invalidated", this.layout, this);
32319 r.on("resized", this.onRegionResized, this);
32320 r.on("collapsed", this.onRegionCollapsed, this);
32321 r.on("expanded", this.onRegionExpanded, this);
32325 * Performs a layout update.
32327 layout : function()
32329 if(this.updating) {
32333 // render all the rebions if they have not been done alreayd?
32334 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32335 if(this.regions[region] && !this.regions[region].bodyEl){
32336 this.regions[region].onRender(this.el)
32340 var size = this.getViewSize();
32341 var w = size.width;
32342 var h = size.height;
32347 //var x = 0, y = 0;
32349 var rs = this.regions;
32350 var north = rs["north"];
32351 var south = rs["south"];
32352 var west = rs["west"];
32353 var east = rs["east"];
32354 var center = rs["center"];
32355 //if(this.hideOnLayout){ // not supported anymore
32356 //c.el.setStyle("display", "none");
32358 if(north && north.isVisible()){
32359 var b = north.getBox();
32360 var m = north.getMargins();
32361 b.width = w - (m.left+m.right);
32364 centerY = b.height + b.y + m.bottom;
32365 centerH -= centerY;
32366 north.updateBox(this.safeBox(b));
32368 if(south && south.isVisible()){
32369 var b = south.getBox();
32370 var m = south.getMargins();
32371 b.width = w - (m.left+m.right);
32373 var totalHeight = (b.height + m.top + m.bottom);
32374 b.y = h - totalHeight + m.top;
32375 centerH -= totalHeight;
32376 south.updateBox(this.safeBox(b));
32378 if(west && west.isVisible()){
32379 var b = west.getBox();
32380 var m = west.getMargins();
32381 b.height = centerH - (m.top+m.bottom);
32383 b.y = centerY + m.top;
32384 var totalWidth = (b.width + m.left + m.right);
32385 centerX += totalWidth;
32386 centerW -= totalWidth;
32387 west.updateBox(this.safeBox(b));
32389 if(east && east.isVisible()){
32390 var b = east.getBox();
32391 var m = east.getMargins();
32392 b.height = centerH - (m.top+m.bottom);
32393 var totalWidth = (b.width + m.left + m.right);
32394 b.x = w - totalWidth + m.left;
32395 b.y = centerY + m.top;
32396 centerW -= totalWidth;
32397 east.updateBox(this.safeBox(b));
32400 var m = center.getMargins();
32402 x: centerX + m.left,
32403 y: centerY + m.top,
32404 width: centerW - (m.left+m.right),
32405 height: centerH - (m.top+m.bottom)
32407 //if(this.hideOnLayout){
32408 //center.el.setStyle("display", "block");
32410 center.updateBox(this.safeBox(centerBox));
32413 this.fireEvent("layout", this);
32417 safeBox : function(box){
32418 box.width = Math.max(0, box.width);
32419 box.height = Math.max(0, box.height);
32424 * Adds a ContentPanel (or subclass) to this layout.
32425 * @param {String} target The target region key (north, south, east, west or center).
32426 * @param {Roo.ContentPanel} panel The panel to add
32427 * @return {Roo.ContentPanel} The added panel
32429 add : function(target, panel){
32431 target = target.toLowerCase();
32432 return this.regions[target].add(panel);
32436 * Remove a ContentPanel (or subclass) to this layout.
32437 * @param {String} target The target region key (north, south, east, west or center).
32438 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32439 * @return {Roo.ContentPanel} The removed panel
32441 remove : function(target, panel){
32442 target = target.toLowerCase();
32443 return this.regions[target].remove(panel);
32447 * Searches all regions for a panel with the specified id
32448 * @param {String} panelId
32449 * @return {Roo.ContentPanel} The panel or null if it wasn't found
32451 findPanel : function(panelId){
32452 var rs = this.regions;
32453 for(var target in rs){
32454 if(typeof rs[target] != "function"){
32455 var p = rs[target].getPanel(panelId);
32465 * Searches all regions for a panel with the specified id and activates (shows) it.
32466 * @param {String/ContentPanel} panelId The panels id or the panel itself
32467 * @return {Roo.ContentPanel} The shown panel or null
32469 showPanel : function(panelId) {
32470 var rs = this.regions;
32471 for(var target in rs){
32472 var r = rs[target];
32473 if(typeof r != "function"){
32474 if(r.hasPanel(panelId)){
32475 return r.showPanel(panelId);
32483 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32484 * @param {Roo.state.Provider} provider (optional) An alternate state provider
32487 restoreState : function(provider){
32489 provider = Roo.state.Manager;
32491 var sm = new Roo.LayoutStateManager();
32492 sm.init(this, provider);
32498 * Adds a xtype elements to the layout.
32502 xtype : 'ContentPanel',
32509 xtype : 'NestedLayoutPanel',
32515 items : [ ... list of content panels or nested layout panels.. ]
32519 * @param {Object} cfg Xtype definition of item to add.
32521 addxtype : function(cfg)
32523 // basically accepts a pannel...
32524 // can accept a layout region..!?!?
32525 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32528 // theory? children can only be panels??
32530 //if (!cfg.xtype.match(/Panel$/)) {
32535 if (typeof(cfg.region) == 'undefined') {
32536 Roo.log("Failed to add Panel, region was not set");
32540 var region = cfg.region;
32546 xitems = cfg.items;
32553 case 'Content': // ContentPanel (el, cfg)
32554 case 'Scroll': // ContentPanel (el, cfg)
32556 cfg.autoCreate = true;
32557 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32559 // var el = this.el.createChild();
32560 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32563 this.add(region, ret);
32567 case 'TreePanel': // our new panel!
32568 cfg.el = this.el.createChild();
32569 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32570 this.add(region, ret);
32575 // create a new Layout (which is a Border Layout...
32577 var clayout = cfg.layout;
32578 clayout.el = this.el.createChild();
32579 clayout.items = clayout.items || [];
32583 // replace this exitems with the clayout ones..
32584 xitems = clayout.items;
32586 // force background off if it's in center...
32587 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32588 cfg.background = false;
32590 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
32593 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32594 //console.log('adding nested layout panel ' + cfg.toSource());
32595 this.add(region, ret);
32596 nb = {}; /// find first...
32601 // needs grid and region
32603 //var el = this.getRegion(region).el.createChild();
32605 *var el = this.el.createChild();
32606 // create the grid first...
32607 cfg.grid.container = el;
32608 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
32611 if (region == 'center' && this.active ) {
32612 cfg.background = false;
32615 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32617 this.add(region, ret);
32619 if (cfg.background) {
32620 // render grid on panel activation (if panel background)
32621 ret.on('activate', function(gp) {
32622 if (!gp.grid.rendered) {
32623 // gp.grid.render(el);
32627 // cfg.grid.render(el);
32633 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
32634 // it was the old xcomponent building that caused this before.
32635 // espeically if border is the top element in the tree.
32645 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32647 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32648 this.add(region, ret);
32652 throw "Can not add '" + cfg.xtype + "' to Border";
32658 this.beginUpdate();
32662 Roo.each(xitems, function(i) {
32663 region = nb && i.region ? i.region : false;
32665 var add = ret.addxtype(i);
32668 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32669 if (!i.background) {
32670 abn[region] = nb[region] ;
32677 // make the last non-background panel active..
32678 //if (nb) { Roo.log(abn); }
32681 for(var r in abn) {
32682 region = this.getRegion(r);
32684 // tried using nb[r], but it does not work..
32686 region.showPanel(abn[r]);
32697 factory : function(cfg)
32700 var validRegions = Roo.bootstrap.layout.Border.regions;
32702 var target = cfg.region;
32705 var r = Roo.bootstrap.layout;
32709 return new r.North(cfg);
32711 return new r.South(cfg);
32713 return new r.East(cfg);
32715 return new r.West(cfg);
32717 return new r.Center(cfg);
32719 throw 'Layout region "'+target+'" not supported.';
32726 * Ext JS Library 1.1.1
32727 * Copyright(c) 2006-2007, Ext JS, LLC.
32729 * Originally Released Under LGPL - original licence link has changed is not relivant.
32732 * <script type="text/javascript">
32736 * @class Roo.bootstrap.layout.Basic
32737 * @extends Roo.util.Observable
32738 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32739 * and does not have a titlebar, tabs or any other features. All it does is size and position
32740 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32741 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32742 * @cfg {string} region the region that it inhabits..
32743 * @cfg {bool} skipConfig skip config?
32747 Roo.bootstrap.layout.Basic = function(config){
32749 this.mgr = config.mgr;
32751 this.position = config.region;
32753 var skipConfig = config.skipConfig;
32757 * @scope Roo.BasicLayoutRegion
32761 * @event beforeremove
32762 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32763 * @param {Roo.LayoutRegion} this
32764 * @param {Roo.ContentPanel} panel The panel
32765 * @param {Object} e The cancel event object
32767 "beforeremove" : true,
32769 * @event invalidated
32770 * Fires when the layout for this region is changed.
32771 * @param {Roo.LayoutRegion} this
32773 "invalidated" : true,
32775 * @event visibilitychange
32776 * Fires when this region is shown or hidden
32777 * @param {Roo.LayoutRegion} this
32778 * @param {Boolean} visibility true or false
32780 "visibilitychange" : true,
32782 * @event paneladded
32783 * Fires when a panel is added.
32784 * @param {Roo.LayoutRegion} this
32785 * @param {Roo.ContentPanel} panel The panel
32787 "paneladded" : true,
32789 * @event panelremoved
32790 * Fires when a panel is removed.
32791 * @param {Roo.LayoutRegion} this
32792 * @param {Roo.ContentPanel} panel The panel
32794 "panelremoved" : true,
32796 * @event beforecollapse
32797 * Fires when this region before collapse.
32798 * @param {Roo.LayoutRegion} this
32800 "beforecollapse" : true,
32803 * Fires when this region is collapsed.
32804 * @param {Roo.LayoutRegion} this
32806 "collapsed" : true,
32809 * Fires when this region is expanded.
32810 * @param {Roo.LayoutRegion} this
32815 * Fires when this region is slid into view.
32816 * @param {Roo.LayoutRegion} this
32818 "slideshow" : true,
32821 * Fires when this region slides out of view.
32822 * @param {Roo.LayoutRegion} this
32824 "slidehide" : true,
32826 * @event panelactivated
32827 * Fires when a panel is activated.
32828 * @param {Roo.LayoutRegion} this
32829 * @param {Roo.ContentPanel} panel The activated panel
32831 "panelactivated" : true,
32834 * Fires when the user resizes this region.
32835 * @param {Roo.LayoutRegion} this
32836 * @param {Number} newSize The new size (width for east/west, height for north/south)
32840 /** A collection of panels in this region. @type Roo.util.MixedCollection */
32841 this.panels = new Roo.util.MixedCollection();
32842 this.panels.getKey = this.getPanelId.createDelegate(this);
32844 this.activePanel = null;
32845 // ensure listeners are added...
32847 if (config.listeners || config.events) {
32848 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32849 listeners : config.listeners || {},
32850 events : config.events || {}
32854 if(skipConfig !== true){
32855 this.applyConfig(config);
32859 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32861 getPanelId : function(p){
32865 applyConfig : function(config){
32866 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32867 this.config = config;
32872 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
32873 * the width, for horizontal (north, south) the height.
32874 * @param {Number} newSize The new width or height
32876 resizeTo : function(newSize){
32877 var el = this.el ? this.el :
32878 (this.activePanel ? this.activePanel.getEl() : null);
32880 switch(this.position){
32883 el.setWidth(newSize);
32884 this.fireEvent("resized", this, newSize);
32888 el.setHeight(newSize);
32889 this.fireEvent("resized", this, newSize);
32895 getBox : function(){
32896 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32899 getMargins : function(){
32900 return this.margins;
32903 updateBox : function(box){
32905 var el = this.activePanel.getEl();
32906 el.dom.style.left = box.x + "px";
32907 el.dom.style.top = box.y + "px";
32908 this.activePanel.setSize(box.width, box.height);
32912 * Returns the container element for this region.
32913 * @return {Roo.Element}
32915 getEl : function(){
32916 return this.activePanel;
32920 * Returns true if this region is currently visible.
32921 * @return {Boolean}
32923 isVisible : function(){
32924 return this.activePanel ? true : false;
32927 setActivePanel : function(panel){
32928 panel = this.getPanel(panel);
32929 if(this.activePanel && this.activePanel != panel){
32930 this.activePanel.setActiveState(false);
32931 this.activePanel.getEl().setLeftTop(-10000,-10000);
32933 this.activePanel = panel;
32934 panel.setActiveState(true);
32936 panel.setSize(this.box.width, this.box.height);
32938 this.fireEvent("panelactivated", this, panel);
32939 this.fireEvent("invalidated");
32943 * Show the specified panel.
32944 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32945 * @return {Roo.ContentPanel} The shown panel or null
32947 showPanel : function(panel){
32948 panel = this.getPanel(panel);
32950 this.setActivePanel(panel);
32956 * Get the active panel for this region.
32957 * @return {Roo.ContentPanel} The active panel or null
32959 getActivePanel : function(){
32960 return this.activePanel;
32964 * Add the passed ContentPanel(s)
32965 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32966 * @return {Roo.ContentPanel} The panel added (if only one was added)
32968 add : function(panel){
32969 if(arguments.length > 1){
32970 for(var i = 0, len = arguments.length; i < len; i++) {
32971 this.add(arguments[i]);
32975 if(this.hasPanel(panel)){
32976 this.showPanel(panel);
32979 var el = panel.getEl();
32980 if(el.dom.parentNode != this.mgr.el.dom){
32981 this.mgr.el.dom.appendChild(el.dom);
32983 if(panel.setRegion){
32984 panel.setRegion(this);
32986 this.panels.add(panel);
32987 el.setStyle("position", "absolute");
32988 if(!panel.background){
32989 this.setActivePanel(panel);
32990 if(this.config.initialSize && this.panels.getCount()==1){
32991 this.resizeTo(this.config.initialSize);
32994 this.fireEvent("paneladded", this, panel);
32999 * Returns true if the panel is in this region.
33000 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33001 * @return {Boolean}
33003 hasPanel : function(panel){
33004 if(typeof panel == "object"){ // must be panel obj
33005 panel = panel.getId();
33007 return this.getPanel(panel) ? true : false;
33011 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33012 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33013 * @param {Boolean} preservePanel Overrides the config preservePanel option
33014 * @return {Roo.ContentPanel} The panel that was removed
33016 remove : function(panel, preservePanel){
33017 panel = this.getPanel(panel);
33022 this.fireEvent("beforeremove", this, panel, e);
33023 if(e.cancel === true){
33026 var panelId = panel.getId();
33027 this.panels.removeKey(panelId);
33032 * Returns the panel specified or null if it's not in this region.
33033 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33034 * @return {Roo.ContentPanel}
33036 getPanel : function(id){
33037 if(typeof id == "object"){ // must be panel obj
33040 return this.panels.get(id);
33044 * Returns this regions position (north/south/east/west/center).
33047 getPosition: function(){
33048 return this.position;
33052 * Ext JS Library 1.1.1
33053 * Copyright(c) 2006-2007, Ext JS, LLC.
33055 * Originally Released Under LGPL - original licence link has changed is not relivant.
33058 * <script type="text/javascript">
33062 * @class Roo.bootstrap.layout.Region
33063 * @extends Roo.bootstrap.layout.Basic
33064 * This class represents a region in a layout manager.
33066 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33067 * @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})
33068 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
33069 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
33070 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
33071 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
33072 * @cfg {String} title The title for the region (overrides panel titles)
33073 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
33074 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33075 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
33076 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33077 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
33078 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33079 * the space available, similar to FireFox 1.5 tabs (defaults to false)
33080 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
33081 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
33082 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
33084 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
33085 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
33086 * @cfg {Boolean} disableTabTips True to disable tab tooltips
33087 * @cfg {Number} width For East/West panels
33088 * @cfg {Number} height For North/South panels
33089 * @cfg {Boolean} split To show the splitter
33090 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
33092 * @cfg {string} cls Extra CSS classes to add to region
33094 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
33095 * @cfg {string} region the region that it inhabits..
33098 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
33099 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
33101 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
33102 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
33103 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
33105 Roo.bootstrap.layout.Region = function(config)
33107 this.applyConfig(config);
33109 var mgr = config.mgr;
33110 var pos = config.region;
33111 config.skipConfig = true;
33112 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33115 this.onRender(mgr.el);
33118 this.visible = true;
33119 this.collapsed = false;
33120 this.unrendered_panels = [];
33123 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33125 position: '', // set by wrapper (eg. north/south etc..)
33126 unrendered_panels : null, // unrendered panels.
33127 createBody : function(){
33128 /** This region's body element
33129 * @type Roo.Element */
33130 this.bodyEl = this.el.createChild({
33132 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33136 onRender: function(ctr, pos)
33138 var dh = Roo.DomHelper;
33139 /** This region's container element
33140 * @type Roo.Element */
33141 this.el = dh.append(ctr.dom, {
33143 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33145 /** This region's title element
33146 * @type Roo.Element */
33148 this.titleEl = dh.append(this.el.dom,
33151 unselectable: "on",
33152 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33154 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
33155 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33158 this.titleEl.enableDisplayMode();
33159 /** This region's title text element
33160 * @type HTMLElement */
33161 this.titleTextEl = this.titleEl.dom.firstChild;
33162 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33164 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33165 this.closeBtn.enableDisplayMode();
33166 this.closeBtn.on("click", this.closeClicked, this);
33167 this.closeBtn.hide();
33169 this.createBody(this.config);
33170 if(this.config.hideWhenEmpty){
33172 this.on("paneladded", this.validateVisibility, this);
33173 this.on("panelremoved", this.validateVisibility, this);
33175 if(this.autoScroll){
33176 this.bodyEl.setStyle("overflow", "auto");
33178 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33180 //if(c.titlebar !== false){
33181 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33182 this.titleEl.hide();
33184 this.titleEl.show();
33185 if(this.config.title){
33186 this.titleTextEl.innerHTML = this.config.title;
33190 if(this.config.collapsed){
33191 this.collapse(true);
33193 if(this.config.hidden){
33197 if (this.unrendered_panels && this.unrendered_panels.length) {
33198 for (var i =0;i< this.unrendered_panels.length; i++) {
33199 this.add(this.unrendered_panels[i]);
33201 this.unrendered_panels = null;
33207 applyConfig : function(c)
33210 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33211 var dh = Roo.DomHelper;
33212 if(c.titlebar !== false){
33213 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33214 this.collapseBtn.on("click", this.collapse, this);
33215 this.collapseBtn.enableDisplayMode();
33217 if(c.showPin === true || this.showPin){
33218 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33219 this.stickBtn.enableDisplayMode();
33220 this.stickBtn.on("click", this.expand, this);
33221 this.stickBtn.hide();
33226 /** This region's collapsed element
33227 * @type Roo.Element */
33230 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33231 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33234 if(c.floatable !== false){
33235 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33236 this.collapsedEl.on("click", this.collapseClick, this);
33239 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33240 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33241 id: "message", unselectable: "on", style:{"float":"left"}});
33242 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33244 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33245 this.expandBtn.on("click", this.expand, this);
33249 if(this.collapseBtn){
33250 this.collapseBtn.setVisible(c.collapsible == true);
33253 this.cmargins = c.cmargins || this.cmargins ||
33254 (this.position == "west" || this.position == "east" ?
33255 {top: 0, left: 2, right:2, bottom: 0} :
33256 {top: 2, left: 0, right:0, bottom: 2});
33258 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33261 this.bottomTabs = c.tabPosition != "top";
33263 this.autoScroll = c.autoScroll || false;
33268 this.duration = c.duration || .30;
33269 this.slideDuration = c.slideDuration || .45;
33274 * Returns true if this region is currently visible.
33275 * @return {Boolean}
33277 isVisible : function(){
33278 return this.visible;
33282 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33283 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
33285 //setCollapsedTitle : function(title){
33286 // title = title || " ";
33287 // if(this.collapsedTitleTextEl){
33288 // this.collapsedTitleTextEl.innerHTML = title;
33292 getBox : function(){
33294 // if(!this.collapsed){
33295 b = this.el.getBox(false, true);
33297 // b = this.collapsedEl.getBox(false, true);
33302 getMargins : function(){
33303 return this.margins;
33304 //return this.collapsed ? this.cmargins : this.margins;
33307 highlight : function(){
33308 this.el.addClass("x-layout-panel-dragover");
33311 unhighlight : function(){
33312 this.el.removeClass("x-layout-panel-dragover");
33315 updateBox : function(box)
33317 if (!this.bodyEl) {
33318 return; // not rendered yet..
33322 if(!this.collapsed){
33323 this.el.dom.style.left = box.x + "px";
33324 this.el.dom.style.top = box.y + "px";
33325 this.updateBody(box.width, box.height);
33327 this.collapsedEl.dom.style.left = box.x + "px";
33328 this.collapsedEl.dom.style.top = box.y + "px";
33329 this.collapsedEl.setSize(box.width, box.height);
33332 this.tabs.autoSizeTabs();
33336 updateBody : function(w, h)
33339 this.el.setWidth(w);
33340 w -= this.el.getBorderWidth("rl");
33341 if(this.config.adjustments){
33342 w += this.config.adjustments[0];
33345 if(h !== null && h > 0){
33346 this.el.setHeight(h);
33347 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33348 h -= this.el.getBorderWidth("tb");
33349 if(this.config.adjustments){
33350 h += this.config.adjustments[1];
33352 this.bodyEl.setHeight(h);
33354 h = this.tabs.syncHeight(h);
33357 if(this.panelSize){
33358 w = w !== null ? w : this.panelSize.width;
33359 h = h !== null ? h : this.panelSize.height;
33361 if(this.activePanel){
33362 var el = this.activePanel.getEl();
33363 w = w !== null ? w : el.getWidth();
33364 h = h !== null ? h : el.getHeight();
33365 this.panelSize = {width: w, height: h};
33366 this.activePanel.setSize(w, h);
33368 if(Roo.isIE && this.tabs){
33369 this.tabs.el.repaint();
33374 * Returns the container element for this region.
33375 * @return {Roo.Element}
33377 getEl : function(){
33382 * Hides this region.
33385 //if(!this.collapsed){
33386 this.el.dom.style.left = "-2000px";
33389 // this.collapsedEl.dom.style.left = "-2000px";
33390 // this.collapsedEl.hide();
33392 this.visible = false;
33393 this.fireEvent("visibilitychange", this, false);
33397 * Shows this region if it was previously hidden.
33400 //if(!this.collapsed){
33403 // this.collapsedEl.show();
33405 this.visible = true;
33406 this.fireEvent("visibilitychange", this, true);
33409 closeClicked : function(){
33410 if(this.activePanel){
33411 this.remove(this.activePanel);
33415 collapseClick : function(e){
33417 e.stopPropagation();
33420 e.stopPropagation();
33426 * Collapses this region.
33427 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33430 collapse : function(skipAnim, skipCheck = false){
33431 if(this.collapsed) {
33435 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33437 this.collapsed = true;
33439 this.split.el.hide();
33441 if(this.config.animate && skipAnim !== true){
33442 this.fireEvent("invalidated", this);
33443 this.animateCollapse();
33445 this.el.setLocation(-20000,-20000);
33447 this.collapsedEl.show();
33448 this.fireEvent("collapsed", this);
33449 this.fireEvent("invalidated", this);
33455 animateCollapse : function(){
33460 * Expands this region if it was previously collapsed.
33461 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33462 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33465 expand : function(e, skipAnim){
33467 e.stopPropagation();
33469 if(!this.collapsed || this.el.hasActiveFx()) {
33473 this.afterSlideIn();
33476 this.collapsed = false;
33477 if(this.config.animate && skipAnim !== true){
33478 this.animateExpand();
33482 this.split.el.show();
33484 this.collapsedEl.setLocation(-2000,-2000);
33485 this.collapsedEl.hide();
33486 this.fireEvent("invalidated", this);
33487 this.fireEvent("expanded", this);
33491 animateExpand : function(){
33495 initTabs : function()
33497 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33499 var ts = new Roo.bootstrap.panel.Tabs({
33500 el: this.bodyEl.dom,
33501 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33502 disableTooltips: this.config.disableTabTips,
33503 toolbar : this.config.toolbar
33506 if(this.config.hideTabs){
33507 ts.stripWrap.setDisplayed(false);
33510 ts.resizeTabs = this.config.resizeTabs === true;
33511 ts.minTabWidth = this.config.minTabWidth || 40;
33512 ts.maxTabWidth = this.config.maxTabWidth || 250;
33513 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33514 ts.monitorResize = false;
33515 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33516 ts.bodyEl.addClass('roo-layout-tabs-body');
33517 this.panels.each(this.initPanelAsTab, this);
33520 initPanelAsTab : function(panel){
33521 var ti = this.tabs.addTab(
33525 this.config.closeOnTab && panel.isClosable(),
33528 if(panel.tabTip !== undefined){
33529 ti.setTooltip(panel.tabTip);
33531 ti.on("activate", function(){
33532 this.setActivePanel(panel);
33535 if(this.config.closeOnTab){
33536 ti.on("beforeclose", function(t, e){
33538 this.remove(panel);
33542 panel.tabItem = ti;
33547 updatePanelTitle : function(panel, title)
33549 if(this.activePanel == panel){
33550 this.updateTitle(title);
33553 var ti = this.tabs.getTab(panel.getEl().id);
33555 if(panel.tabTip !== undefined){
33556 ti.setTooltip(panel.tabTip);
33561 updateTitle : function(title){
33562 if(this.titleTextEl && !this.config.title){
33563 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
33567 setActivePanel : function(panel)
33569 panel = this.getPanel(panel);
33570 if(this.activePanel && this.activePanel != panel){
33571 this.activePanel.setActiveState(false);
33573 this.activePanel = panel;
33574 panel.setActiveState(true);
33575 if(this.panelSize){
33576 panel.setSize(this.panelSize.width, this.panelSize.height);
33579 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33581 this.updateTitle(panel.getTitle());
33583 this.fireEvent("invalidated", this);
33585 this.fireEvent("panelactivated", this, panel);
33589 * Shows the specified panel.
33590 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33591 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33593 showPanel : function(panel)
33595 panel = this.getPanel(panel);
33598 var tab = this.tabs.getTab(panel.getEl().id);
33599 if(tab.isHidden()){
33600 this.tabs.unhideTab(tab.id);
33604 this.setActivePanel(panel);
33611 * Get the active panel for this region.
33612 * @return {Roo.ContentPanel} The active panel or null
33614 getActivePanel : function(){
33615 return this.activePanel;
33618 validateVisibility : function(){
33619 if(this.panels.getCount() < 1){
33620 this.updateTitle(" ");
33621 this.closeBtn.hide();
33624 if(!this.isVisible()){
33631 * Adds the passed ContentPanel(s) to this region.
33632 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33633 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33635 add : function(panel)
33637 if(arguments.length > 1){
33638 for(var i = 0, len = arguments.length; i < len; i++) {
33639 this.add(arguments[i]);
33644 // if we have not been rendered yet, then we can not really do much of this..
33645 if (!this.bodyEl) {
33646 this.unrendered_panels.push(panel);
33653 if(this.hasPanel(panel)){
33654 this.showPanel(panel);
33657 panel.setRegion(this);
33658 this.panels.add(panel);
33659 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33660 // sinle panel - no tab...?? would it not be better to render it with the tabs,
33661 // and hide them... ???
33662 this.bodyEl.dom.appendChild(panel.getEl().dom);
33663 if(panel.background !== true){
33664 this.setActivePanel(panel);
33666 this.fireEvent("paneladded", this, panel);
33673 this.initPanelAsTab(panel);
33677 if(panel.background !== true){
33678 this.tabs.activate(panel.getEl().id);
33680 this.fireEvent("paneladded", this, panel);
33685 * Hides the tab for the specified panel.
33686 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33688 hidePanel : function(panel){
33689 if(this.tabs && (panel = this.getPanel(panel))){
33690 this.tabs.hideTab(panel.getEl().id);
33695 * Unhides the tab for a previously hidden panel.
33696 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33698 unhidePanel : function(panel){
33699 if(this.tabs && (panel = this.getPanel(panel))){
33700 this.tabs.unhideTab(panel.getEl().id);
33704 clearPanels : function(){
33705 while(this.panels.getCount() > 0){
33706 this.remove(this.panels.first());
33711 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33712 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33713 * @param {Boolean} preservePanel Overrides the config preservePanel option
33714 * @return {Roo.ContentPanel} The panel that was removed
33716 remove : function(panel, preservePanel)
33718 panel = this.getPanel(panel);
33723 this.fireEvent("beforeremove", this, panel, e);
33724 if(e.cancel === true){
33727 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33728 var panelId = panel.getId();
33729 this.panels.removeKey(panelId);
33731 document.body.appendChild(panel.getEl().dom);
33734 this.tabs.removeTab(panel.getEl().id);
33735 }else if (!preservePanel){
33736 this.bodyEl.dom.removeChild(panel.getEl().dom);
33738 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33739 var p = this.panels.first();
33740 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33741 tempEl.appendChild(p.getEl().dom);
33742 this.bodyEl.update("");
33743 this.bodyEl.dom.appendChild(p.getEl().dom);
33745 this.updateTitle(p.getTitle());
33747 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33748 this.setActivePanel(p);
33750 panel.setRegion(null);
33751 if(this.activePanel == panel){
33752 this.activePanel = null;
33754 if(this.config.autoDestroy !== false && preservePanel !== true){
33755 try{panel.destroy();}catch(e){}
33757 this.fireEvent("panelremoved", this, panel);
33762 * Returns the TabPanel component used by this region
33763 * @return {Roo.TabPanel}
33765 getTabs : function(){
33769 createTool : function(parentEl, className){
33770 var btn = Roo.DomHelper.append(parentEl, {
33772 cls: "x-layout-tools-button",
33775 cls: "roo-layout-tools-button-inner " + className,
33779 btn.addClassOnOver("roo-layout-tools-button-over");
33784 * Ext JS Library 1.1.1
33785 * Copyright(c) 2006-2007, Ext JS, LLC.
33787 * Originally Released Under LGPL - original licence link has changed is not relivant.
33790 * <script type="text/javascript">
33796 * @class Roo.SplitLayoutRegion
33797 * @extends Roo.LayoutRegion
33798 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33800 Roo.bootstrap.layout.Split = function(config){
33801 this.cursor = config.cursor;
33802 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33805 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33807 splitTip : "Drag to resize.",
33808 collapsibleSplitTip : "Drag to resize. Double click to hide.",
33809 useSplitTips : false,
33811 applyConfig : function(config){
33812 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33815 onRender : function(ctr,pos) {
33817 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33818 if(!this.config.split){
33823 var splitEl = Roo.DomHelper.append(ctr.dom, {
33825 id: this.el.id + "-split",
33826 cls: "roo-layout-split roo-layout-split-"+this.position,
33829 /** The SplitBar for this region
33830 * @type Roo.SplitBar */
33831 // does not exist yet...
33832 Roo.log([this.position, this.orientation]);
33834 this.split = new Roo.bootstrap.SplitBar({
33835 dragElement : splitEl,
33836 resizingElement: this.el,
33837 orientation : this.orientation
33840 this.split.on("moved", this.onSplitMove, this);
33841 this.split.useShim = this.config.useShim === true;
33842 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33843 if(this.useSplitTips){
33844 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33846 //if(config.collapsible){
33847 // this.split.el.on("dblclick", this.collapse, this);
33850 if(typeof this.config.minSize != "undefined"){
33851 this.split.minSize = this.config.minSize;
33853 if(typeof this.config.maxSize != "undefined"){
33854 this.split.maxSize = this.config.maxSize;
33856 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33857 this.hideSplitter();
33862 getHMaxSize : function(){
33863 var cmax = this.config.maxSize || 10000;
33864 var center = this.mgr.getRegion("center");
33865 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33868 getVMaxSize : function(){
33869 var cmax = this.config.maxSize || 10000;
33870 var center = this.mgr.getRegion("center");
33871 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33874 onSplitMove : function(split, newSize){
33875 this.fireEvent("resized", this, newSize);
33879 * Returns the {@link Roo.SplitBar} for this region.
33880 * @return {Roo.SplitBar}
33882 getSplitBar : function(){
33887 this.hideSplitter();
33888 Roo.bootstrap.layout.Split.superclass.hide.call(this);
33891 hideSplitter : function(){
33893 this.split.el.setLocation(-2000,-2000);
33894 this.split.el.hide();
33900 this.split.el.show();
33902 Roo.bootstrap.layout.Split.superclass.show.call(this);
33905 beforeSlide: function(){
33906 if(Roo.isGecko){// firefox overflow auto bug workaround
33907 this.bodyEl.clip();
33909 this.tabs.bodyEl.clip();
33911 if(this.activePanel){
33912 this.activePanel.getEl().clip();
33914 if(this.activePanel.beforeSlide){
33915 this.activePanel.beforeSlide();
33921 afterSlide : function(){
33922 if(Roo.isGecko){// firefox overflow auto bug workaround
33923 this.bodyEl.unclip();
33925 this.tabs.bodyEl.unclip();
33927 if(this.activePanel){
33928 this.activePanel.getEl().unclip();
33929 if(this.activePanel.afterSlide){
33930 this.activePanel.afterSlide();
33936 initAutoHide : function(){
33937 if(this.autoHide !== false){
33938 if(!this.autoHideHd){
33939 var st = new Roo.util.DelayedTask(this.slideIn, this);
33940 this.autoHideHd = {
33941 "mouseout": function(e){
33942 if(!e.within(this.el, true)){
33946 "mouseover" : function(e){
33952 this.el.on(this.autoHideHd);
33956 clearAutoHide : function(){
33957 if(this.autoHide !== false){
33958 this.el.un("mouseout", this.autoHideHd.mouseout);
33959 this.el.un("mouseover", this.autoHideHd.mouseover);
33963 clearMonitor : function(){
33964 Roo.get(document).un("click", this.slideInIf, this);
33967 // these names are backwards but not changed for compat
33968 slideOut : function(){
33969 if(this.isSlid || this.el.hasActiveFx()){
33972 this.isSlid = true;
33973 if(this.collapseBtn){
33974 this.collapseBtn.hide();
33976 this.closeBtnState = this.closeBtn.getStyle('display');
33977 this.closeBtn.hide();
33979 this.stickBtn.show();
33982 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33983 this.beforeSlide();
33984 this.el.setStyle("z-index", 10001);
33985 this.el.slideIn(this.getSlideAnchor(), {
33986 callback: function(){
33988 this.initAutoHide();
33989 Roo.get(document).on("click", this.slideInIf, this);
33990 this.fireEvent("slideshow", this);
33997 afterSlideIn : function(){
33998 this.clearAutoHide();
33999 this.isSlid = false;
34000 this.clearMonitor();
34001 this.el.setStyle("z-index", "");
34002 if(this.collapseBtn){
34003 this.collapseBtn.show();
34005 this.closeBtn.setStyle('display', this.closeBtnState);
34007 this.stickBtn.hide();
34009 this.fireEvent("slidehide", this);
34012 slideIn : function(cb){
34013 if(!this.isSlid || this.el.hasActiveFx()){
34017 this.isSlid = false;
34018 this.beforeSlide();
34019 this.el.slideOut(this.getSlideAnchor(), {
34020 callback: function(){
34021 this.el.setLeftTop(-10000, -10000);
34023 this.afterSlideIn();
34031 slideInIf : function(e){
34032 if(!e.within(this.el)){
34037 animateCollapse : function(){
34038 this.beforeSlide();
34039 this.el.setStyle("z-index", 20000);
34040 var anchor = this.getSlideAnchor();
34041 this.el.slideOut(anchor, {
34042 callback : function(){
34043 this.el.setStyle("z-index", "");
34044 this.collapsedEl.slideIn(anchor, {duration:.3});
34046 this.el.setLocation(-10000,-10000);
34048 this.fireEvent("collapsed", this);
34055 animateExpand : function(){
34056 this.beforeSlide();
34057 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34058 this.el.setStyle("z-index", 20000);
34059 this.collapsedEl.hide({
34062 this.el.slideIn(this.getSlideAnchor(), {
34063 callback : function(){
34064 this.el.setStyle("z-index", "");
34067 this.split.el.show();
34069 this.fireEvent("invalidated", this);
34070 this.fireEvent("expanded", this);
34098 getAnchor : function(){
34099 return this.anchors[this.position];
34102 getCollapseAnchor : function(){
34103 return this.canchors[this.position];
34106 getSlideAnchor : function(){
34107 return this.sanchors[this.position];
34110 getAlignAdj : function(){
34111 var cm = this.cmargins;
34112 switch(this.position){
34128 getExpandAdj : function(){
34129 var c = this.collapsedEl, cm = this.cmargins;
34130 switch(this.position){
34132 return [-(cm.right+c.getWidth()+cm.left), 0];
34135 return [cm.right+c.getWidth()+cm.left, 0];
34138 return [0, -(cm.top+cm.bottom+c.getHeight())];
34141 return [0, cm.top+cm.bottom+c.getHeight()];
34147 * Ext JS Library 1.1.1
34148 * Copyright(c) 2006-2007, Ext JS, LLC.
34150 * Originally Released Under LGPL - original licence link has changed is not relivant.
34153 * <script type="text/javascript">
34156 * These classes are private internal classes
34158 Roo.bootstrap.layout.Center = function(config){
34159 config.region = "center";
34160 Roo.bootstrap.layout.Region.call(this, config);
34161 this.visible = true;
34162 this.minWidth = config.minWidth || 20;
34163 this.minHeight = config.minHeight || 20;
34166 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34168 // center panel can't be hidden
34172 // center panel can't be hidden
34175 getMinWidth: function(){
34176 return this.minWidth;
34179 getMinHeight: function(){
34180 return this.minHeight;
34193 Roo.bootstrap.layout.North = function(config)
34195 config.region = 'north';
34196 config.cursor = 'n-resize';
34198 Roo.bootstrap.layout.Split.call(this, config);
34202 this.split.placement = Roo.bootstrap.SplitBar.TOP;
34203 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34204 this.split.el.addClass("roo-layout-split-v");
34206 var size = config.initialSize || config.height;
34207 if(typeof size != "undefined"){
34208 this.el.setHeight(size);
34211 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34213 orientation: Roo.bootstrap.SplitBar.VERTICAL,
34217 getBox : function(){
34218 if(this.collapsed){
34219 return this.collapsedEl.getBox();
34221 var box = this.el.getBox();
34223 box.height += this.split.el.getHeight();
34228 updateBox : function(box){
34229 if(this.split && !this.collapsed){
34230 box.height -= this.split.el.getHeight();
34231 this.split.el.setLeft(box.x);
34232 this.split.el.setTop(box.y+box.height);
34233 this.split.el.setWidth(box.width);
34235 if(this.collapsed){
34236 this.updateBody(box.width, null);
34238 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34246 Roo.bootstrap.layout.South = function(config){
34247 config.region = 'south';
34248 config.cursor = 's-resize';
34249 Roo.bootstrap.layout.Split.call(this, config);
34251 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34252 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34253 this.split.el.addClass("roo-layout-split-v");
34255 var size = config.initialSize || config.height;
34256 if(typeof size != "undefined"){
34257 this.el.setHeight(size);
34261 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34262 orientation: Roo.bootstrap.SplitBar.VERTICAL,
34263 getBox : function(){
34264 if(this.collapsed){
34265 return this.collapsedEl.getBox();
34267 var box = this.el.getBox();
34269 var sh = this.split.el.getHeight();
34276 updateBox : function(box){
34277 if(this.split && !this.collapsed){
34278 var sh = this.split.el.getHeight();
34281 this.split.el.setLeft(box.x);
34282 this.split.el.setTop(box.y-sh);
34283 this.split.el.setWidth(box.width);
34285 if(this.collapsed){
34286 this.updateBody(box.width, null);
34288 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34292 Roo.bootstrap.layout.East = function(config){
34293 config.region = "east";
34294 config.cursor = "e-resize";
34295 Roo.bootstrap.layout.Split.call(this, config);
34297 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34298 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34299 this.split.el.addClass("roo-layout-split-h");
34301 var size = config.initialSize || config.width;
34302 if(typeof size != "undefined"){
34303 this.el.setWidth(size);
34306 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34307 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34308 getBox : function(){
34309 if(this.collapsed){
34310 return this.collapsedEl.getBox();
34312 var box = this.el.getBox();
34314 var sw = this.split.el.getWidth();
34321 updateBox : function(box){
34322 if(this.split && !this.collapsed){
34323 var sw = this.split.el.getWidth();
34325 this.split.el.setLeft(box.x);
34326 this.split.el.setTop(box.y);
34327 this.split.el.setHeight(box.height);
34330 if(this.collapsed){
34331 this.updateBody(null, box.height);
34333 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34337 Roo.bootstrap.layout.West = function(config){
34338 config.region = "west";
34339 config.cursor = "w-resize";
34341 Roo.bootstrap.layout.Split.call(this, config);
34343 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34344 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34345 this.split.el.addClass("roo-layout-split-h");
34349 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34350 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34352 onRender: function(ctr, pos)
34354 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34355 var size = this.config.initialSize || this.config.width;
34356 if(typeof size != "undefined"){
34357 this.el.setWidth(size);
34361 getBox : function(){
34362 if(this.collapsed){
34363 return this.collapsedEl.getBox();
34365 var box = this.el.getBox();
34367 box.width += this.split.el.getWidth();
34372 updateBox : function(box){
34373 if(this.split && !this.collapsed){
34374 var sw = this.split.el.getWidth();
34376 this.split.el.setLeft(box.x+box.width);
34377 this.split.el.setTop(box.y);
34378 this.split.el.setHeight(box.height);
34380 if(this.collapsed){
34381 this.updateBody(null, box.height);
34383 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34386 Roo.namespace("Roo.bootstrap.panel");/*
34388 * Ext JS Library 1.1.1
34389 * Copyright(c) 2006-2007, Ext JS, LLC.
34391 * Originally Released Under LGPL - original licence link has changed is not relivant.
34394 * <script type="text/javascript">
34397 * @class Roo.ContentPanel
34398 * @extends Roo.util.Observable
34399 * A basic ContentPanel element.
34400 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
34401 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
34402 * @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
34403 * @cfg {Boolean} closable True if the panel can be closed/removed
34404 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
34405 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34406 * @cfg {Toolbar} toolbar A toolbar for this panel
34407 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
34408 * @cfg {String} title The title for this panel
34409 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34410 * @cfg {String} url Calls {@link #setUrl} with this value
34411 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34412 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
34413 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
34414 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
34415 * @cfg {Boolean} badges render the badges
34418 * Create a new ContentPanel.
34419 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34420 * @param {String/Object} config A string to set only the title or a config object
34421 * @param {String} content (optional) Set the HTML content for this panel
34422 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34424 Roo.bootstrap.panel.Content = function( config){
34426 this.tpl = config.tpl || false;
34428 var el = config.el;
34429 var content = config.content;
34431 if(config.autoCreate){ // xtype is available if this is called from factory
34434 this.el = Roo.get(el);
34435 if(!this.el && config && config.autoCreate){
34436 if(typeof config.autoCreate == "object"){
34437 if(!config.autoCreate.id){
34438 config.autoCreate.id = config.id||el;
34440 this.el = Roo.DomHelper.append(document.body,
34441 config.autoCreate, true);
34443 var elcfg = { tag: "div",
34444 cls: "roo-layout-inactive-content",
34448 elcfg.html = config.html;
34452 this.el = Roo.DomHelper.append(document.body, elcfg , true);
34455 this.closable = false;
34456 this.loaded = false;
34457 this.active = false;
34460 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
34462 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
34464 this.wrapEl = this.el; //this.el.wrap();
34466 if (config.toolbar.items) {
34467 ti = config.toolbar.items ;
34468 delete config.toolbar.items ;
34472 this.toolbar.render(this.wrapEl, 'before');
34473 for(var i =0;i < ti.length;i++) {
34474 // Roo.log(['add child', items[i]]);
34475 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34477 this.toolbar.items = nitems;
34478 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34479 delete config.toolbar;
34483 // xtype created footer. - not sure if will work as we normally have to render first..
34484 if (this.footer && !this.footer.el && this.footer.xtype) {
34485 if (!this.wrapEl) {
34486 this.wrapEl = this.el.wrap();
34489 this.footer.container = this.wrapEl.createChild();
34491 this.footer = Roo.factory(this.footer, Roo);
34496 if(typeof config == "string"){
34497 this.title = config;
34499 Roo.apply(this, config);
34503 this.resizeEl = Roo.get(this.resizeEl, true);
34505 this.resizeEl = this.el;
34507 // handle view.xtype
34515 * Fires when this panel is activated.
34516 * @param {Roo.ContentPanel} this
34520 * @event deactivate
34521 * Fires when this panel is activated.
34522 * @param {Roo.ContentPanel} this
34524 "deactivate" : true,
34528 * Fires when this panel is resized if fitToFrame is true.
34529 * @param {Roo.ContentPanel} this
34530 * @param {Number} width The width after any component adjustments
34531 * @param {Number} height The height after any component adjustments
34537 * Fires when this tab is created
34538 * @param {Roo.ContentPanel} this
34549 if(this.autoScroll){
34550 this.resizeEl.setStyle("overflow", "auto");
34552 // fix randome scrolling
34553 //this.el.on('scroll', function() {
34554 // Roo.log('fix random scolling');
34555 // this.scrollTo('top',0);
34558 content = content || this.content;
34560 this.setContent(content);
34562 if(config && config.url){
34563 this.setUrl(this.url, this.params, this.loadOnce);
34568 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34570 if (this.view && typeof(this.view.xtype) != 'undefined') {
34571 this.view.el = this.el.appendChild(document.createElement("div"));
34572 this.view = Roo.factory(this.view);
34573 this.view.render && this.view.render(false, '');
34577 this.fireEvent('render', this);
34580 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34584 setRegion : function(region){
34585 this.region = region;
34586 this.setActiveClass(region && !this.background);
34590 setActiveClass: function(state)
34593 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
34594 this.el.setStyle('position','relative');
34596 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
34597 this.el.setStyle('position', 'absolute');
34602 * Returns the toolbar for this Panel if one was configured.
34603 * @return {Roo.Toolbar}
34605 getToolbar : function(){
34606 return this.toolbar;
34609 setActiveState : function(active)
34611 this.active = active;
34612 this.setActiveClass(active);
34614 this.fireEvent("deactivate", this);
34616 this.fireEvent("activate", this);
34620 * Updates this panel's element
34621 * @param {String} content The new content
34622 * @param {Boolean} loadScripts (optional) true to look for and process scripts
34624 setContent : function(content, loadScripts){
34625 this.el.update(content, loadScripts);
34628 ignoreResize : function(w, h){
34629 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34632 this.lastSize = {width: w, height: h};
34637 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34638 * @return {Roo.UpdateManager} The UpdateManager
34640 getUpdateManager : function(){
34641 return this.el.getUpdateManager();
34644 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34645 * @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:
34648 url: "your-url.php",
34649 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34650 callback: yourFunction,
34651 scope: yourObject, //(optional scope)
34654 text: "Loading...",
34659 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34660 * 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.
34661 * @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}
34662 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34663 * @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.
34664 * @return {Roo.ContentPanel} this
34667 var um = this.el.getUpdateManager();
34668 um.update.apply(um, arguments);
34674 * 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.
34675 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34676 * @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)
34677 * @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)
34678 * @return {Roo.UpdateManager} The UpdateManager
34680 setUrl : function(url, params, loadOnce){
34681 if(this.refreshDelegate){
34682 this.removeListener("activate", this.refreshDelegate);
34684 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34685 this.on("activate", this.refreshDelegate);
34686 return this.el.getUpdateManager();
34689 _handleRefresh : function(url, params, loadOnce){
34690 if(!loadOnce || !this.loaded){
34691 var updater = this.el.getUpdateManager();
34692 updater.update(url, params, this._setLoaded.createDelegate(this));
34696 _setLoaded : function(){
34697 this.loaded = true;
34701 * Returns this panel's id
34704 getId : function(){
34709 * Returns this panel's element - used by regiosn to add.
34710 * @return {Roo.Element}
34712 getEl : function(){
34713 return this.wrapEl || this.el;
34718 adjustForComponents : function(width, height)
34720 //Roo.log('adjustForComponents ');
34721 if(this.resizeEl != this.el){
34722 width -= this.el.getFrameWidth('lr');
34723 height -= this.el.getFrameWidth('tb');
34726 var te = this.toolbar.getEl();
34727 height -= te.getHeight();
34728 te.setWidth(width);
34731 var te = this.footer.getEl();
34732 Roo.log("footer:" + te.getHeight());
34734 height -= te.getHeight();
34735 te.setWidth(width);
34739 if(this.adjustments){
34740 width += this.adjustments[0];
34741 height += this.adjustments[1];
34743 return {"width": width, "height": height};
34746 setSize : function(width, height){
34747 if(this.fitToFrame && !this.ignoreResize(width, height)){
34748 if(this.fitContainer && this.resizeEl != this.el){
34749 this.el.setSize(width, height);
34751 var size = this.adjustForComponents(width, height);
34752 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34753 this.fireEvent('resize', this, size.width, size.height);
34758 * Returns this panel's title
34761 getTitle : function(){
34766 * Set this panel's title
34767 * @param {String} title
34769 setTitle : function(title){
34770 this.title = title;
34772 this.region.updatePanelTitle(this, title);
34777 * Returns true is this panel was configured to be closable
34778 * @return {Boolean}
34780 isClosable : function(){
34781 return this.closable;
34784 beforeSlide : function(){
34786 this.resizeEl.clip();
34789 afterSlide : function(){
34791 this.resizeEl.unclip();
34795 * Force a content refresh from the URL specified in the {@link #setUrl} method.
34796 * Will fail silently if the {@link #setUrl} method has not been called.
34797 * This does not activate the panel, just updates its content.
34799 refresh : function(){
34800 if(this.refreshDelegate){
34801 this.loaded = false;
34802 this.refreshDelegate();
34807 * Destroys this panel
34809 destroy : function(){
34810 this.el.removeAllListeners();
34811 var tempEl = document.createElement("span");
34812 tempEl.appendChild(this.el.dom);
34813 tempEl.innerHTML = "";
34819 * form - if the content panel contains a form - this is a reference to it.
34820 * @type {Roo.form.Form}
34824 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34825 * This contains a reference to it.
34831 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34841 * @param {Object} cfg Xtype definition of item to add.
34845 getChildContainer: function () {
34846 return this.getEl();
34851 var ret = new Roo.factory(cfg);
34856 if (cfg.xtype.match(/^Form$/)) {
34859 //if (this.footer) {
34860 // el = this.footer.container.insertSibling(false, 'before');
34862 el = this.el.createChild();
34865 this.form = new Roo.form.Form(cfg);
34868 if ( this.form.allItems.length) {
34869 this.form.render(el.dom);
34873 // should only have one of theses..
34874 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34875 // views.. should not be just added - used named prop 'view''
34877 cfg.el = this.el.appendChild(document.createElement("div"));
34880 var ret = new Roo.factory(cfg);
34882 ret.render && ret.render(false, ''); // render blank..
34892 * @class Roo.bootstrap.panel.Grid
34893 * @extends Roo.bootstrap.panel.Content
34895 * Create a new GridPanel.
34896 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34897 * @param {Object} config A the config object
34903 Roo.bootstrap.panel.Grid = function(config)
34907 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34908 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34910 config.el = this.wrapper;
34911 //this.el = this.wrapper;
34913 if (config.container) {
34914 // ctor'ed from a Border/panel.grid
34917 this.wrapper.setStyle("overflow", "hidden");
34918 this.wrapper.addClass('roo-grid-container');
34923 if(config.toolbar){
34924 var tool_el = this.wrapper.createChild();
34925 this.toolbar = Roo.factory(config.toolbar);
34927 if (config.toolbar.items) {
34928 ti = config.toolbar.items ;
34929 delete config.toolbar.items ;
34933 this.toolbar.render(tool_el);
34934 for(var i =0;i < ti.length;i++) {
34935 // Roo.log(['add child', items[i]]);
34936 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34938 this.toolbar.items = nitems;
34940 delete config.toolbar;
34943 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34944 config.grid.scrollBody = true;;
34945 config.grid.monitorWindowResize = false; // turn off autosizing
34946 config.grid.autoHeight = false;
34947 config.grid.autoWidth = false;
34949 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34951 if (config.background) {
34952 // render grid on panel activation (if panel background)
34953 this.on('activate', function(gp) {
34954 if (!gp.grid.rendered) {
34955 gp.grid.render(this.wrapper);
34956 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34961 this.grid.render(this.wrapper);
34962 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34965 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34966 // ??? needed ??? config.el = this.wrapper;
34971 // xtype created footer. - not sure if will work as we normally have to render first..
34972 if (this.footer && !this.footer.el && this.footer.xtype) {
34974 var ctr = this.grid.getView().getFooterPanel(true);
34975 this.footer.dataSource = this.grid.dataSource;
34976 this.footer = Roo.factory(this.footer, Roo);
34977 this.footer.render(ctr);
34987 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34988 getId : function(){
34989 return this.grid.id;
34993 * Returns the grid for this panel
34994 * @return {Roo.bootstrap.Table}
34996 getGrid : function(){
35000 setSize : function(width, height){
35001 if(!this.ignoreResize(width, height)){
35002 var grid = this.grid;
35003 var size = this.adjustForComponents(width, height);
35004 var gridel = grid.getGridEl();
35005 gridel.setSize(size.width, size.height);
35007 var thd = grid.getGridEl().select('thead',true).first();
35008 var tbd = grid.getGridEl().select('tbody', true).first();
35010 tbd.setSize(width, height - thd.getHeight());
35019 beforeSlide : function(){
35020 this.grid.getView().scroller.clip();
35023 afterSlide : function(){
35024 this.grid.getView().scroller.unclip();
35027 destroy : function(){
35028 this.grid.destroy();
35030 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
35035 * @class Roo.bootstrap.panel.Nest
35036 * @extends Roo.bootstrap.panel.Content
35038 * Create a new Panel, that can contain a layout.Border.
35041 * @param {Roo.BorderLayout} layout The layout for this panel
35042 * @param {String/Object} config A string to set only the title or a config object
35044 Roo.bootstrap.panel.Nest = function(config)
35046 // construct with only one argument..
35047 /* FIXME - implement nicer consturctors
35048 if (layout.layout) {
35050 layout = config.layout;
35051 delete config.layout;
35053 if (layout.xtype && !layout.getEl) {
35054 // then layout needs constructing..
35055 layout = Roo.factory(layout, Roo);
35059 config.el = config.layout.getEl();
35061 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35063 config.layout.monitorWindowResize = false; // turn off autosizing
35064 this.layout = config.layout;
35065 this.layout.getEl().addClass("roo-layout-nested-layout");
35072 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35074 setSize : function(width, height){
35075 if(!this.ignoreResize(width, height)){
35076 var size = this.adjustForComponents(width, height);
35077 var el = this.layout.getEl();
35078 if (size.height < 1) {
35079 el.setWidth(size.width);
35081 el.setSize(size.width, size.height);
35083 var touch = el.dom.offsetWidth;
35084 this.layout.layout();
35085 // ie requires a double layout on the first pass
35086 if(Roo.isIE && !this.initialized){
35087 this.initialized = true;
35088 this.layout.layout();
35093 // activate all subpanels if not currently active..
35095 setActiveState : function(active){
35096 this.active = active;
35097 this.setActiveClass(active);
35100 this.fireEvent("deactivate", this);
35104 this.fireEvent("activate", this);
35105 // not sure if this should happen before or after..
35106 if (!this.layout) {
35107 return; // should not happen..
35110 for (var r in this.layout.regions) {
35111 reg = this.layout.getRegion(r);
35112 if (reg.getActivePanel()) {
35113 //reg.showPanel(reg.getActivePanel()); // force it to activate..
35114 reg.setActivePanel(reg.getActivePanel());
35117 if (!reg.panels.length) {
35120 reg.showPanel(reg.getPanel(0));
35129 * Returns the nested BorderLayout for this panel
35130 * @return {Roo.BorderLayout}
35132 getLayout : function(){
35133 return this.layout;
35137 * Adds a xtype elements to the layout of the nested panel
35141 xtype : 'ContentPanel',
35148 xtype : 'NestedLayoutPanel',
35154 items : [ ... list of content panels or nested layout panels.. ]
35158 * @param {Object} cfg Xtype definition of item to add.
35160 addxtype : function(cfg) {
35161 return this.layout.addxtype(cfg);
35166 * Ext JS Library 1.1.1
35167 * Copyright(c) 2006-2007, Ext JS, LLC.
35169 * Originally Released Under LGPL - original licence link has changed is not relivant.
35172 * <script type="text/javascript">
35175 * @class Roo.TabPanel
35176 * @extends Roo.util.Observable
35177 * A lightweight tab container.
35181 // basic tabs 1, built from existing content
35182 var tabs = new Roo.TabPanel("tabs1");
35183 tabs.addTab("script", "View Script");
35184 tabs.addTab("markup", "View Markup");
35185 tabs.activate("script");
35187 // more advanced tabs, built from javascript
35188 var jtabs = new Roo.TabPanel("jtabs");
35189 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35191 // set up the UpdateManager
35192 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35193 var updater = tab2.getUpdateManager();
35194 updater.setDefaultUrl("ajax1.htm");
35195 tab2.on('activate', updater.refresh, updater, true);
35197 // Use setUrl for Ajax loading
35198 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35199 tab3.setUrl("ajax2.htm", null, true);
35202 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35205 jtabs.activate("jtabs-1");
35208 * Create a new TabPanel.
35209 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35210 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35212 Roo.bootstrap.panel.Tabs = function(config){
35214 * The container element for this TabPanel.
35215 * @type Roo.Element
35217 this.el = Roo.get(config.el);
35220 if(typeof config == "boolean"){
35221 this.tabPosition = config ? "bottom" : "top";
35223 Roo.apply(this, config);
35227 if(this.tabPosition == "bottom"){
35228 this.bodyEl = Roo.get(this.createBody(this.el.dom));
35229 this.el.addClass("roo-tabs-bottom");
35231 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35232 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35233 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35235 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35237 if(this.tabPosition != "bottom"){
35238 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35239 * @type Roo.Element
35241 this.bodyEl = Roo.get(this.createBody(this.el.dom));
35242 this.el.addClass("roo-tabs-top");
35246 this.bodyEl.setStyle("position", "relative");
35248 this.active = null;
35249 this.activateDelegate = this.activate.createDelegate(this);
35254 * Fires when the active tab changes
35255 * @param {Roo.TabPanel} this
35256 * @param {Roo.TabPanelItem} activePanel The new active tab
35260 * @event beforetabchange
35261 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35262 * @param {Roo.TabPanel} this
35263 * @param {Object} e Set cancel to true on this object to cancel the tab change
35264 * @param {Roo.TabPanelItem} tab The tab being changed to
35266 "beforetabchange" : true
35269 Roo.EventManager.onWindowResize(this.onResize, this);
35270 this.cpad = this.el.getPadding("lr");
35271 this.hiddenCount = 0;
35274 // toolbar on the tabbar support...
35275 if (this.toolbar) {
35276 alert("no toolbar support yet");
35277 this.toolbar = false;
35279 var tcfg = this.toolbar;
35280 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
35281 this.toolbar = new Roo.Toolbar(tcfg);
35282 if (Roo.isSafari) {
35283 var tbl = tcfg.container.child('table', true);
35284 tbl.setAttribute('width', '100%');
35292 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35295 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35297 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35299 tabPosition : "top",
35301 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35303 currentTabWidth : 0,
35305 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35309 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35313 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35315 preferredTabWidth : 175,
35317 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35319 resizeTabs : false,
35321 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35323 monitorResize : true,
35325 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
35330 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35331 * @param {String} id The id of the div to use <b>or create</b>
35332 * @param {String} text The text for the tab
35333 * @param {String} content (optional) Content to put in the TabPanelItem body
35334 * @param {Boolean} closable (optional) True to create a close icon on the tab
35335 * @return {Roo.TabPanelItem} The created TabPanelItem
35337 addTab : function(id, text, content, closable, tpl)
35339 var item = new Roo.bootstrap.panel.TabItem({
35343 closable : closable,
35346 this.addTabItem(item);
35348 item.setContent(content);
35354 * Returns the {@link Roo.TabPanelItem} with the specified id/index
35355 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35356 * @return {Roo.TabPanelItem}
35358 getTab : function(id){
35359 return this.items[id];
35363 * Hides the {@link Roo.TabPanelItem} with the specified id/index
35364 * @param {String/Number} id The id or index of the TabPanelItem to hide.
35366 hideTab : function(id){
35367 var t = this.items[id];
35370 this.hiddenCount++;
35371 this.autoSizeTabs();
35376 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35377 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35379 unhideTab : function(id){
35380 var t = this.items[id];
35382 t.setHidden(false);
35383 this.hiddenCount--;
35384 this.autoSizeTabs();
35389 * Adds an existing {@link Roo.TabPanelItem}.
35390 * @param {Roo.TabPanelItem} item The TabPanelItem to add
35392 addTabItem : function(item){
35393 this.items[item.id] = item;
35394 this.items.push(item);
35395 // if(this.resizeTabs){
35396 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35397 // this.autoSizeTabs();
35399 // item.autoSize();
35404 * Removes a {@link Roo.TabPanelItem}.
35405 * @param {String/Number} id The id or index of the TabPanelItem to remove.
35407 removeTab : function(id){
35408 var items = this.items;
35409 var tab = items[id];
35410 if(!tab) { return; }
35411 var index = items.indexOf(tab);
35412 if(this.active == tab && items.length > 1){
35413 var newTab = this.getNextAvailable(index);
35418 this.stripEl.dom.removeChild(tab.pnode.dom);
35419 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
35420 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
35422 items.splice(index, 1);
35423 delete this.items[tab.id];
35424 tab.fireEvent("close", tab);
35425 tab.purgeListeners();
35426 this.autoSizeTabs();
35429 getNextAvailable : function(start){
35430 var items = this.items;
35432 // look for a next tab that will slide over to
35433 // replace the one being removed
35434 while(index < items.length){
35435 var item = items[++index];
35436 if(item && !item.isHidden()){
35440 // if one isn't found select the previous tab (on the left)
35443 var item = items[--index];
35444 if(item && !item.isHidden()){
35452 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
35453 * @param {String/Number} id The id or index of the TabPanelItem to disable.
35455 disableTab : function(id){
35456 var tab = this.items[id];
35457 if(tab && this.active != tab){
35463 * Enables a {@link Roo.TabPanelItem} that is disabled.
35464 * @param {String/Number} id The id or index of the TabPanelItem to enable.
35466 enableTab : function(id){
35467 var tab = this.items[id];
35472 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
35473 * @param {String/Number} id The id or index of the TabPanelItem to activate.
35474 * @return {Roo.TabPanelItem} The TabPanelItem.
35476 activate : function(id){
35477 var tab = this.items[id];
35481 if(tab == this.active || tab.disabled){
35485 this.fireEvent("beforetabchange", this, e, tab);
35486 if(e.cancel !== true && !tab.disabled){
35488 this.active.hide();
35490 this.active = this.items[id];
35491 this.active.show();
35492 this.fireEvent("tabchange", this, this.active);
35498 * Gets the active {@link Roo.TabPanelItem}.
35499 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35501 getActiveTab : function(){
35502 return this.active;
35506 * Updates the tab body element to fit the height of the container element
35507 * for overflow scrolling
35508 * @param {Number} targetHeight (optional) Override the starting height from the elements height
35510 syncHeight : function(targetHeight){
35511 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35512 var bm = this.bodyEl.getMargins();
35513 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35514 this.bodyEl.setHeight(newHeight);
35518 onResize : function(){
35519 if(this.monitorResize){
35520 this.autoSizeTabs();
35525 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35527 beginUpdate : function(){
35528 this.updating = true;
35532 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35534 endUpdate : function(){
35535 this.updating = false;
35536 this.autoSizeTabs();
35540 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35542 autoSizeTabs : function(){
35543 var count = this.items.length;
35544 var vcount = count - this.hiddenCount;
35545 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35548 var w = Math.max(this.el.getWidth() - this.cpad, 10);
35549 var availWidth = Math.floor(w / vcount);
35550 var b = this.stripBody;
35551 if(b.getWidth() > w){
35552 var tabs = this.items;
35553 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35554 if(availWidth < this.minTabWidth){
35555 /*if(!this.sleft){ // incomplete scrolling code
35556 this.createScrollButtons();
35559 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35562 if(this.currentTabWidth < this.preferredTabWidth){
35563 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35569 * Returns the number of tabs in this TabPanel.
35572 getCount : function(){
35573 return this.items.length;
35577 * Resizes all the tabs to the passed width
35578 * @param {Number} The new width
35580 setTabWidth : function(width){
35581 this.currentTabWidth = width;
35582 for(var i = 0, len = this.items.length; i < len; i++) {
35583 if(!this.items[i].isHidden()) {
35584 this.items[i].setWidth(width);
35590 * Destroys this TabPanel
35591 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
35593 destroy : function(removeEl){
35594 Roo.EventManager.removeResizeListener(this.onResize, this);
35595 for(var i = 0, len = this.items.length; i < len; i++){
35596 this.items[i].purgeListeners();
35598 if(removeEl === true){
35599 this.el.update("");
35604 createStrip : function(container)
35606 var strip = document.createElement("nav");
35607 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
35608 container.appendChild(strip);
35612 createStripList : function(strip)
35614 // div wrapper for retard IE
35615 // returns the "tr" element.
35616 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
35617 //'<div class="x-tabs-strip-wrap">'+
35618 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
35619 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
35620 return strip.firstChild; //.firstChild.firstChild.firstChild;
35622 createBody : function(container)
35624 var body = document.createElement("div");
35625 Roo.id(body, "tab-body");
35626 //Roo.fly(body).addClass("x-tabs-body");
35627 Roo.fly(body).addClass("tab-content");
35628 container.appendChild(body);
35631 createItemBody :function(bodyEl, id){
35632 var body = Roo.getDom(id);
35634 body = document.createElement("div");
35637 //Roo.fly(body).addClass("x-tabs-item-body");
35638 Roo.fly(body).addClass("tab-pane");
35639 bodyEl.insertBefore(body, bodyEl.firstChild);
35643 createStripElements : function(stripEl, text, closable, tpl)
35645 var td = document.createElement("li"); // was td..
35648 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
35651 stripEl.appendChild(td);
35653 td.className = "x-tabs-closable";
35654 if(!this.closeTpl){
35655 this.closeTpl = new Roo.Template(
35656 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35657 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
35658 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
35661 var el = this.closeTpl.overwrite(td, {"text": text});
35662 var close = el.getElementsByTagName("div")[0];
35663 var inner = el.getElementsByTagName("em")[0];
35664 return {"el": el, "close": close, "inner": inner};
35667 // not sure what this is..
35668 // if(!this.tabTpl){
35669 //this.tabTpl = new Roo.Template(
35670 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35671 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
35673 // this.tabTpl = new Roo.Template(
35674 // '<a href="#">' +
35675 // '<span unselectable="on"' +
35676 // (this.disableTooltips ? '' : ' title="{text}"') +
35677 // ' >{text}</span></a>'
35683 var template = tpl || this.tabTpl || false;
35687 template = new Roo.Template(
35689 '<span unselectable="on"' +
35690 (this.disableTooltips ? '' : ' title="{text}"') +
35691 ' >{text}</span></a>'
35695 switch (typeof(template)) {
35699 template = new Roo.Template(template);
35705 var el = template.overwrite(td, {"text": text});
35707 var inner = el.getElementsByTagName("span")[0];
35709 return {"el": el, "inner": inner};
35717 * @class Roo.TabPanelItem
35718 * @extends Roo.util.Observable
35719 * Represents an individual item (tab plus body) in a TabPanel.
35720 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35721 * @param {String} id The id of this TabPanelItem
35722 * @param {String} text The text for the tab of this TabPanelItem
35723 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35725 Roo.bootstrap.panel.TabItem = function(config){
35727 * The {@link Roo.TabPanel} this TabPanelItem belongs to
35728 * @type Roo.TabPanel
35730 this.tabPanel = config.panel;
35732 * The id for this TabPanelItem
35735 this.id = config.id;
35737 this.disabled = false;
35739 this.text = config.text;
35741 this.loaded = false;
35742 this.closable = config.closable;
35745 * The body element for this TabPanelItem.
35746 * @type Roo.Element
35748 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35749 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35750 this.bodyEl.setStyle("display", "block");
35751 this.bodyEl.setStyle("zoom", "1");
35752 //this.hideAction();
35754 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
35756 this.el = Roo.get(els.el);
35757 this.inner = Roo.get(els.inner, true);
35758 this.textEl = Roo.get(this.el.dom.firstChild, true);
35759 this.pnode = Roo.get(els.el.parentNode, true);
35760 this.el.on("mousedown", this.onTabMouseDown, this);
35761 this.el.on("click", this.onTabClick, this);
35763 if(config.closable){
35764 var c = Roo.get(els.close, true);
35765 c.dom.title = this.closeText;
35766 c.addClassOnOver("close-over");
35767 c.on("click", this.closeClick, this);
35773 * Fires when this tab becomes the active tab.
35774 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35775 * @param {Roo.TabPanelItem} this
35779 * @event beforeclose
35780 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35781 * @param {Roo.TabPanelItem} this
35782 * @param {Object} e Set cancel to true on this object to cancel the close.
35784 "beforeclose": true,
35787 * Fires when this tab is closed.
35788 * @param {Roo.TabPanelItem} this
35792 * @event deactivate
35793 * Fires when this tab is no longer the active tab.
35794 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35795 * @param {Roo.TabPanelItem} this
35797 "deactivate" : true
35799 this.hidden = false;
35801 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35804 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35806 purgeListeners : function(){
35807 Roo.util.Observable.prototype.purgeListeners.call(this);
35808 this.el.removeAllListeners();
35811 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35814 this.pnode.addClass("active");
35817 this.tabPanel.stripWrap.repaint();
35819 this.fireEvent("activate", this.tabPanel, this);
35823 * Returns true if this tab is the active tab.
35824 * @return {Boolean}
35826 isActive : function(){
35827 return this.tabPanel.getActiveTab() == this;
35831 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35834 this.pnode.removeClass("active");
35836 this.fireEvent("deactivate", this.tabPanel, this);
35839 hideAction : function(){
35840 this.bodyEl.hide();
35841 this.bodyEl.setStyle("position", "absolute");
35842 this.bodyEl.setLeft("-20000px");
35843 this.bodyEl.setTop("-20000px");
35846 showAction : function(){
35847 this.bodyEl.setStyle("position", "relative");
35848 this.bodyEl.setTop("");
35849 this.bodyEl.setLeft("");
35850 this.bodyEl.show();
35854 * Set the tooltip for the tab.
35855 * @param {String} tooltip The tab's tooltip
35857 setTooltip : function(text){
35858 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35859 this.textEl.dom.qtip = text;
35860 this.textEl.dom.removeAttribute('title');
35862 this.textEl.dom.title = text;
35866 onTabClick : function(e){
35867 e.preventDefault();
35868 this.tabPanel.activate(this.id);
35871 onTabMouseDown : function(e){
35872 e.preventDefault();
35873 this.tabPanel.activate(this.id);
35876 getWidth : function(){
35877 return this.inner.getWidth();
35880 setWidth : function(width){
35881 var iwidth = width - this.pnode.getPadding("lr");
35882 this.inner.setWidth(iwidth);
35883 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35884 this.pnode.setWidth(width);
35888 * Show or hide the tab
35889 * @param {Boolean} hidden True to hide or false to show.
35891 setHidden : function(hidden){
35892 this.hidden = hidden;
35893 this.pnode.setStyle("display", hidden ? "none" : "");
35897 * Returns true if this tab is "hidden"
35898 * @return {Boolean}
35900 isHidden : function(){
35901 return this.hidden;
35905 * Returns the text for this tab
35908 getText : function(){
35912 autoSize : function(){
35913 //this.el.beginMeasure();
35914 this.textEl.setWidth(1);
35916 * #2804 [new] Tabs in Roojs
35917 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35919 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35920 //this.el.endMeasure();
35924 * Sets the text for the tab (Note: this also sets the tooltip text)
35925 * @param {String} text The tab's text and tooltip
35927 setText : function(text){
35929 this.textEl.update(text);
35930 this.setTooltip(text);
35931 //if(!this.tabPanel.resizeTabs){
35932 // this.autoSize();
35936 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35938 activate : function(){
35939 this.tabPanel.activate(this.id);
35943 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35945 disable : function(){
35946 if(this.tabPanel.active != this){
35947 this.disabled = true;
35948 this.pnode.addClass("disabled");
35953 * Enables this TabPanelItem if it was previously disabled.
35955 enable : function(){
35956 this.disabled = false;
35957 this.pnode.removeClass("disabled");
35961 * Sets the content for this TabPanelItem.
35962 * @param {String} content The content
35963 * @param {Boolean} loadScripts true to look for and load scripts
35965 setContent : function(content, loadScripts){
35966 this.bodyEl.update(content, loadScripts);
35970 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35971 * @return {Roo.UpdateManager} The UpdateManager
35973 getUpdateManager : function(){
35974 return this.bodyEl.getUpdateManager();
35978 * Set a URL to be used to load the content for this TabPanelItem.
35979 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35980 * @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)
35981 * @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)
35982 * @return {Roo.UpdateManager} The UpdateManager
35984 setUrl : function(url, params, loadOnce){
35985 if(this.refreshDelegate){
35986 this.un('activate', this.refreshDelegate);
35988 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35989 this.on("activate", this.refreshDelegate);
35990 return this.bodyEl.getUpdateManager();
35994 _handleRefresh : function(url, params, loadOnce){
35995 if(!loadOnce || !this.loaded){
35996 var updater = this.bodyEl.getUpdateManager();
35997 updater.update(url, params, this._setLoaded.createDelegate(this));
36002 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
36003 * Will fail silently if the setUrl method has not been called.
36004 * This does not activate the panel, just updates its content.
36006 refresh : function(){
36007 if(this.refreshDelegate){
36008 this.loaded = false;
36009 this.refreshDelegate();
36014 _setLoaded : function(){
36015 this.loaded = true;
36019 closeClick : function(e){
36022 this.fireEvent("beforeclose", this, o);
36023 if(o.cancel !== true){
36024 this.tabPanel.removeTab(this.id);
36028 * The text displayed in the tooltip for the close icon.
36031 closeText : "Close this tab"